How can a domain aggregate entity become its own aggregate?

Doğa Barış Özdemir
4 min readJul 13, 2023

Even well designed and defined domains should change over time while it lives. Business and customer needs change as long as domain live. So as developers we should re-evaluate domain aggregates to satisfy new demands. Example DDD data architecture shown below chart.

Example aggregate, aggregate root entity, value object, entity

In this chart we see ticket aggregate which contains flow entity as child local entity. At some point we would like to move local flow entity to its own aggregate because its meaning changed since we define. There can be more reasons to make this transformation such as read & write optimization, wrongful design at first, localization need of aggregate atomicity etc. In this post I will try to explain steps to transform aggregate entity to make its new aggregate.

In sample scenario write / persistent datas are stored on Cassandra. On the read side datas are stored on ElasticSearch. Let’s consider we have a ticket management system. Ticket management domains can be summarized with two things in general. One is ticket aggregate which holds information about tickets. Other one is flow to determine how tickets should run business flows. With this scenario we have a local flow entity in ticket aggregate which stores flow’s version at that time and flow aggregate that holds current business flow.

Ticket Aggregate

At some point we decide to make versions of flows and not localize flow entity in ticket aggregate. In practice it’s not fully doable for every situation. For example if you don’t want to serve flow’s name with secondary call from flow aggregate then ticket aggregate’s flow entity still should store name property with id. Contrary to usual I like to share result to-do list first then to talk about it. Because creating such to-do lists goes through brainstorming processes that take longer than expected.

Transformation to-do list can be summarized as follows:

  • Create temporary ticketflow data for migration.
  • Adding version property to ticket aggregate’s flow entity.
  • Adding version property to flow aggregate.
  • Develop endpoints to create flows with versions.
  • Develop endpoints to serve flows with given flow id & version number.
  • Make sure new created tickets hold version numbers correctly.
  • Determine versions of temporary ticketflow.
  • Set tickets’ flow versions.
  • Set old flow aggregate versions.
  • Remove unnecessary flow entity fields on ticket.
  • Activate flow version businesses to related backend and frontend services.

I will discuss each point on the to-do list in the paragraphs below.

Create temporary ticketflow data for migration.

First of all we should determine versions of flow which is hard task to do. We can simplify ticket & flow with creating temporary ticketflow data in elasticsearch. Easiest way to check differences of any kind of data is comparing hashes. In the light of this information we should store ticket id, flow entity and flow hash on ticketflow temporary index in elastic.

Adding version property to ticket aggregate’s flow entity.

Adding version property to flow aggregate.

We should define version property on ticket and flow aggregate to be sure they are going to store version info. They can start their life with dummy null values.

Develop endpoints to create flows with versions.

Develop endpoints to serve flows with given flow id & version number.

Flow aggregate is going to hold version information so we should develop endpoints to serve flow data with given id & version number. Newly created flows should be created with version info and it should be mandatory.

Determine versions of temporary ticketflow.

To determine versions we can easily run an aggregation query in elastic to get every versions of every flows.

Example elasticsearch query to get every versions of every flows

We set size 0 because we won’t need any kind of document. We only need aggregation’s results. “ticketflows” aggregates flow ids and “flow_versions” aggregates flows’ versions. With result of query we can easliy go through flow id and its flow hashes to set versions.

Set tickets’ flow versions.

Create old flow aggregate versions.

Once we set versions in temporary ticketflow index we can update ticket aggregate’s flow entity’s version. Also we can create flow aggregate’s older versions.

Remove unnecessary flow fields on ticket.

Activate flow version businesses to related backend and frontend services.

When everything set up data-wise we can remove unnecessary flow entity fields on ticket aggregate. Then we can change related backend and frontend logics interested in ticket & flow. If we don’t need flow version information on ticket we can delete it too.

Ticket & Flow Aggregates end of the day

With the example scenario and to-do points we could manage transform ticket aggregate’s flow entity to become its own aggregate. Flow aggregate holds versions on its own and can serve historical data with this work. After this type of transformation overhead load on ticket domain is reduced. Old flow aggregate defined better in ticket management domain with the transformation. Flow domain and ticket domain can be seperated in doman driven design manner. This practice improves ubiquitous language between developers and product owners.

Any feedback regarding this topic is well-received. Thank you for your reading.

--

--