Go architectures · 3 MIN READ

DDD: bounded contexts and aggregates

Model language and invariants before choosing storage or transport.

Model language and invariants before choosing storage or transport. Domain-driven design is a modeling approach, not a demand for microservices. Trace the worked Go example, then compare the benefit with the cost before applying this design to your own project.

Architecture · A MAP OF THE IDEA

  1. Context defines vocabulary
  2. Aggregate validates a command
  3. Repository persists atomically

Useful when

  • Business rules change frequently
  • Different domains use the same word differently

Watch out

  • Modeling requires collaboration with domain experts
  • Concurrency needs persistence-level enforcement

Implementation & operation cost: High

Fit depends on the task

Small tool or CLI1/5
Domain-rich application5/5
Independent distributed services4/5

Editorial suitability, not benchmarks or a universal ranking. Styles describe different axes and can be combined.

01

The design decision

Domain-driven design is a modeling approach, not a demand for microservices. A bounded context defines where a model and its language apply. An aggregate protects a consistency boundary: its public operations should prevent invalid state transitions.

02

A complete Go example

Read this program from the entry point and follow the calls shown in the diagram. The output is included at the end so you can check the behavior without a compiler.

go
package main

import (
	"errors"
	"fmt"
)

type Basket struct{ items int }

func (b *Basket) Add(quantity int) error {
	if quantity <= 0 {
		return errors.New("positive quantity required")
	}
	if b.items+quantity > 10 {
		return errors.New("basket limit")
	}
	b.items += quantity
	return nil
}

func main() {
	b := &Basket{}
	fmt.Println(b.Add(3))
	fmt.Println(b.Add(8))
	fmt.Println(b.items)
}

// Output:
// <nil>
// basket limit
// 3
Read the code

The Basket operation checks the invariant before mutation, so a rejected addition leaves three items. This illustrates an aggregate rule, not durable storage. Concurrent database writes need transactions or version checks to preserve the same invariant.

03

Tradeoffs and the simpler alternative

Use richer models where business language and rules justify them. A reporting query can return a projection without reconstructing every aggregate. Too many tiny entities and repositories obscure straightforward CRUD; bounded contexts can remain packages in one executable.

04

Model invariants before naming entities

A value object is defined by its value, while an entity has identity over time. An aggregate is a consistency boundary whose root protects invariants, not a graph of everything related by a foreign key. For an order, accepting a line may check quantity and price rules before mutation. Keep those rules enforceable without an HTTP handler. A bounded context defines where terms have one meaning: customer in billing need not be the same model as customer in support.

05

Coordinate aggregates with explicit consistency

Keep a transaction small enough to protect the invariant it owns. If placing an order and reserving remote stock cannot be atomic, model pending, confirmed and rejected states and define compensating actions. Use version checks when concurrent writers can otherwise overwrite each other. Domain events describe facts that already happened; publishing them durably requires an outbox or another defined handoff. Calling a broker in the middle of mutation does not make the database and broker commit together.

06

Use domain modeling selectively

Start with scenarios from domain experts and encode examples as tests: duplicate line, cancelled order, price change and concurrent update. CRUD administration often needs a simple data model rather than repositories, aggregates and domain services. Introduce an anti-corruption adapter when integrating a foreign model so its vocabulary does not leak everywhere. DDD has a high communication and modeling cost; it pays when rules and language are complex, not merely when a project has many tables.

TAKE THIS WITH YOU

Protects invariants within a consistency boundary. Use richer models where business language and rules justify them. A reporting query can return a projection without reconstructing every aggregate. Too many tiny entities and repositories obscure straightforward CRUD; bounded contexts can remain packages in one executable.

Check your understanding

Which statement best describes DDD: bounded contexts and aggregates?

Go to the source

Progress stays in this browser. No account needed.