Skip to content

Multiple Aggregates in Same Module ​

In the same module, it's normal for many related aggregates to appear. When business is known to iterate rapidly, if we still write like a running account without extra consideration for variable relationships, the code will soon become hard to maintain.

Assume Order and Book aggregates have very close business relationships. Here's the code:

ts
const d = createDomainDesigner()
const i = d.info

const UpstreamSystem = d.system('UpstreamSystem', 'Upstream system')

// Book "constant pool"
const bookValues = {
  ISBN: i.id(
    'isbn',
    `International Standard Book Number
      This value serves as an id and can uniquely identify a book`
  ),
  bookName: i.valueObj('bookName', 'Book name'),
  seriesName: i.valueObj('seriesName', 'Series name'),
  bookPrice: i.valueObj('bookPrice', 'Book price'),
}

// Order "constant pool"
const orderValues = {
  orderSequence: i.id(
    'orderSequence',
    `Order serial number
      This value serves as an id, determined during requirements research`
  ),
  globalSequence: i.valueObj(
    'globalSequence',
    d.note`Global sequence number
      During requirements research, it was determined that this value is uniformly generated by an ${UpstreamSystem} interface`
  ),
  bookSets: i.func(
    'bookSets',
    [bookValues.ISBN, bookValues.bookPrice, 'count'],
    'Order collection'
  ),
}

const BookPutInStorage = d.event('BookPutInStorage', [bookValues.ISBN], 'New book put in storage')

const PutBookInStorage = d.command(
  'PutBookInStorage',
  [bookValues.ISBN, 'time'],
  'Put book in storage command'
)

const BookAgg = d.agg(
  'bookAgg',
  [bookValues.ISBN, bookValues.bookName, bookValues.seriesName],
  'Book aggregate'
)

const OrderAgg = d.agg(
  'OrderAgg',
  [i.func('Product collection', [bookValues.ISBN])],
  'Order aggregate'
)

In this code snippet, we define two constant pools: bookValues and orderValues. Put values that need reuse into them, and other nodes can take from here when defining the same values.

Benefits of this approach:

  • All references share the same source, no repetitive definitions needed.

  • This "constant pool" also serves as a glossary. For terms that aren't easy to understand or that team members might interpret differently, we can describe them in detail here

Flexibly Use TypeScript's Module Mechanism ​

Assume the Order aggregate depends on the Book aggregate. Code can be split like this:

ts
import { d } from './order-detail/common'
import './order-detail/book'
import './order-detail/order'
export default d
ts
import { d, i } from './common'
import { bookValues } from './book'

const OrderPlaced = d.event(
  'OrderPlaced',
  [i.id('orderSequence', 'Order serial number'), bookValues.ISBN],
  'Order placed'
)
ts
import { d, i } from './common'

export const bookValues = {
  ISBN: i.id('isbn', 'International Standard Book Number'),
}
const BookAgg = d.agg(
  'bookAgg',
  [
    /* ... */
  ],
  'Book aggregate'
)
ts
import { createDomainDesigner } from '@ddd-tool/domain-designer-core'
export const d = createDomainDesigner()
export const i = d.info