DDD Tutorial - Modelling Domain Relationship and Domain Service

Written on 02 September 2016

This tutorial is an excerpt of the actual DDD modelling process of Value++ (Agile project management software) using Domain Map.

Business functionality: Remove a user from an organization

A quite straight forward functionality, however there is a little catch: an organization should always have at least 1 admin left. Basically, if the user is the last admin, the operation should fail (with an error message).

Modelling

The bounded context

We’re dealing with users and organization so, the relevant context is Membership (switch to it).

Applying CQS

The functionality clearly changes the business state, so we are in a Command case. Let’s create it in Domain Map, Add -> Command Use Case -> enter “Remove user from organization”.

Identifying the business state change

Well, the resulting change is about a (site) member who got removed from an organization. Add-> Event -> enter MemberRemovedFromOrganization . That’s our event and we need to find out the relevant details. Obviously, we need to know the member id and organization id. However, we need one of them to be the entity id, basically the id of the concept that the change is associated with. But both Member and Organization concepts are entities, so who is in ‘charge’? What concept ‘cares’ more about the operation?

It turns out the operation makes most sense for the Organization concept, so, the entity id will be the organization id. Click on the Events tab, then click on the new event to enter the details we’ve found out.

event

Identifying the aggregate

We know the result, but we need to enforce the business rules. First, we need to identify the aggregate, but in this situation we’re dealing with a weak relationship, we are ‘destroying’ an instance of that relationship and the aggregate should reflect that relationship, not Member or Organization. At this point, we don’t have that relationship represented in Domain Map so let’s create it now.

Click on the Entities tab, and then click on Organization. Then click on the “+” icon from the Relationships box.

dialog

Click on “+” from With Concept box then select Member. Let’s name this relationship “Members can be part of an organization”.

dialog2

Click “Create” and the new relationship should appear

dialog3

This relationship will appear on both Member and Organization details and the whole point is to acknowledge that these 2 concepts can be (dis)associated. However, this is not an aggregate, we still have to identify it and we do that as part of the current business case.

Click on Command Use Cases tab then click on Remove user from organization. Click “+” from Uses command model of and select the newly created relationship. What this tell us is that the command case will change an instance of the relationship and in this case, we ‘destroy’ it. And this aggregate will be in charge of that. Click on it to go to the details.

Inside the aggregate, you’ll see both (implicit) concepts: Member and Organization, then in the default outcome, select MemberRemovedFromOrganization.

agg

We’re done with the aggregate.

Using a Domain Service

But that’s not all! Remember the business rule that the last admin of the group must not be removed. So we need to enforce that and the best place is a Domain Service (DS). Why not a part of the aggregate, you ask? Because this rule is unrelated to the consistency of the relationship. It is an ‘outside” (the aggregate) rule so, we make it part of a DS. Even more, it’s a ‘permission’ kinda of rule, basically it tells the application service if the business case can continue. If not, it doesn’t even get to the aggregate part.

Add -> Domain Service -> enter: Can user be removed from organization . Then, go to the command case, click on “+” from Involves box to link to the domain service. Then click on the service name link to go to its details. In the Business behaviour box, write all the details that we need.

service

Well, I’ve kinda included a bit of implementation details here but hey, it doesn’t hurt anyone, especially if only developers (a domain modeller is usually one) will see it. But if you want to go even deeper with coding details (like “we should use a couple of query objects etc”), I suggest to put that in a note (the note icon next to the name).

Conclusion

This was a more complex case, but in the end we have our command case which involves a domain service (with all required details), we’re aware of a relationship between concepts and we know the aggregate and the event. Best part? No specific code was written, but with all this information available, it’s easier now to write an implementation using your favourite stack.

You can check out the model from this tutorial here.

DDD Tutorial - Modelling Query Cases

Written on 31 August 2016

This tutorial is an excerpt of the actual DDD modelling process of Value++ (Agile project management software) using Domain Map.

Business functionality: We want to list the members of an organization

In order to see or manage the members of an organization we need to get a list of them. This is a very simple and straight-forward query business case. We only need the following data: member id, member name, member role.

Modelling

The bounded context

The font end of Value++ is a single page app (SPA) that communicates with an API server. The backend app doesn’t have a UI layer/component, but it does have an API one. Every query functionality invoked directly by the client will be implemented as part of this component. So, in Domain Map, we need a API context that needs to be created (click on the “+” icon below the selected bounded context button).

CQS in action

It was obvious from the start that we’re dealing with a Query, no surprises here. But what does it mean from a modelling point of view? Well, it means no changes (events) or aggregates are required. All we really need is some read-only data i.e a read model. But this is still an application model, not a persistence model, which is an implementation detail.

All the read models of the domain are about what data is required by a specific functionality and they stay the same, regardless of how you choose to implement persistence. Actually, the more query cases you’re modelling, the better you get an idea of how the persistence should be designed. In DDD and Domain Map it’s all about information.

But first, let’s create our Query case: Add -> Query Use Case and enter the name: “Get organization members”. After that, click on the Query Use Cases tab, then click on the newly created item to see the details.

Now, create our read model: Add -> Read Model then enter the name: “Organization member”. Once it’s created, click on the “+” icon from the Involves box then select our read model. This tells us that the query case uses the read model.

query links read model

Click on “Organization member” link to go to its details. Here, we just write the structure of our read model.

read model

One thing I like when dealing with read models is that they are just simple data structures and quite literally we’re actually designing the class structure that we’ll use in our code. A lot of Query cases and read models are just CRUD bits so it’s best to treat them as such. But we don’t care much about implementation details when we’re modelling so, for technical details, it’s best to create a note. In this case, I don’t have anything useful to say so, I leave things as they are.

And that’s all you need to model a very simple query business case. In some case more than one read model can be involved, so add as many as required, but keep in mind they still need to represent semantics for the business.

Bonus case: Get all organizations a user is member of

Very similar to the one above, we need to return a list of “Organization”, where the needed data is: org id, org name, member role.

We’re already in the Api context, no need to change it. Add -> Query Use Case and enter: “Get user organizations”. Then go to the query use cases tab (if you’re not there already), select the new case then Add -> Read Model and enter: “Organization info”. This name might change in the future, but for now it’s good enough. Link the read model to the query case, using the Involve box. Go to the read model details and write its structure.

So far, it’s identical to the previous case (to be honest it gets boring after a while, but hey, it’s the functionality the business needs and it’s simple enough). But now, let’s go a step further. What we want is to add more information about where the read model data comes from. Since changes are expressed as events, the source of data for read models can only be events.

In this particular case, we already have a relevant event Organization Created that we’ve identified in the previous tutorial . Click on “+” icon from the Update by box then select that event. Now we know where to look for that data and you can have as many events as you need.

read model 2

This is also, one of the reasons why we model first as much as we can, and only after that we think about implementation. Knowing the read models, their structures and their connected events tells us a great deal of information about how to design and efficient persistence (read) model.

As a bonus, if you go to Organization created details you’ll see the list of read models updated by the event. This is very useful if in the future the event structure changes (you’ll know at a glance how many read models will be broken :P ).

event

You can check out the result of this tutorial here.

DDD Tutorial - Modelling "Create Organization"

Written on 27 August 2016

This tutorial is an excerpt of the actual DDD modelling process of Value++ (Agile project management software) using Domain Map.

Business functionality: Create Organization

We want to create an organization, similar to what GitHub has. An organization is a group of members who can participate in the organization’s projects. Organization name must be unique, at most 50 characters length. An organization should have at least 1 member in admin role. A user can be part of any number of organizations.

Modelling

The bounded context

First things first, we need to identify the bounded context (BC). We’re dealing with users and groups so, I’d say the proper bounded context should be something like Membership. This is not core domain functionality, but I prefer to look at the whole app as a group of bounded contexts (anything that has a model with boundaries is a BC). Some of them belong to the Domain, others are just application specific. The Membership is app specific, but that doesn’t change the fact that it has its own specific model that needs to be maintainable.

At this point we need to create the Membership BC.

create context

Click on the add icon then write the name ‘Membership’ then press the Enter key. We’ve created and switched to the new context.

Using CQS

We need to identify the type of the business case: command or query. In this case, we’re dealing with a command, since the result of the action changes the existing business state. Let’s create the command case. Click on the blue Add button (left of tabs).

create command write name

Press ‘Enter’ or click ‘Create’. The command case is created.

Identifying the event

Any business state change is represented by an event. In this case the change is that an organization has been created, so we create our OrganizationCreated event.

create event

Next, click on the Events tab, then on the Organization created event. Now, it’s time to find out the change details i.e the event structure. In this case they’re simple.

event details

Click ‘Save’ when finished.

Identifying the concepts and the aggregate

We know the command, we know the result, now it’s time to find out the what are the models involved in generating that change. It’s pretty obvious that we have the concept of Organization which is an Entity. Let’s create it.

event details

Now, we need to identify a model relevant for our business case. Click the Command Use Cases tab, then click on Create Organization. Press the ‘+’ icon in the Uses Command Model of box. It should look like below

link command to entity

Click on Organization to link the command case to the concept in charge of doing changes. Next, click on the Organization link to go to the aggregate which is specific to that command.

model link

Now, it’s time to determine the aggregate composition (relevant to this business case, only!) and business constraints. The important thing here is to understand that we’re identifying a model, we’re not designing a class with properties. In our case, we know that the organization needs a name and an admin. We start with the name, by creating a Value Object (by clicking Add-> Value Object). Once we have it, we link it with the aggregate ( click on the ‘+’ icon in the Aggregate composition box).

aggregate to value object

Now, let’s write the business rules for the organization name. Click on it to go to the Value Object’s details.

value object details

I wrote the constraints as a regex. Feel free to write things how you want them as they communicate the rules that the values must respect. Click ‘Save’ then click on Create organization link at the right to navigate back to the aggregate.

Now, we know that the organization must have an (authenticated) user in the admin role. This is a strong relationship between the Organization concept and the Member concept. We’re going to represent this relationship by making the aggregate reference the Member entity, which doesn’t exist so we have to create it first (Add -> Entity). Then, click on “+” in the Aggregate composition box to link the Member concept.

aggregate details

You see that we have that “All are required” rule, this means all the components of the aggregate must be present in valid form. When writing code, the Member reference will be implemented as a MemberId value object.

There’s no mention of the “organization name must be unique” rule. That’s because it’s not an aggregate rule, but it’s still a rule that should be enforced. At this moment, Domain Map doesn’t have any specific tools for business rules that aren’t part of an aggregate or a Domain Service but we still want to record this information.

So, we’ll create a Note , named “Other rules”.

create note

You can create notes to record anything you want, including implementation details.

note details

Close the note and scroll down a bit. You should see the Outcomes box, with a Default Outcome option. Click on it, then click on the “+” icon from the Events box. Click on the Organization created event to specify the default outcome (change) generated by the aggregate.

outcome

Since an aggregate controls change and the change is represented as an event, we always want to know which aggregate generated which change. In this case we only have one outcome, but in other cases, we can have multiple outcomes, each with their own event(s). This association also allows us to navigate to event details and back. Feel free to navigate around a bit, clicking on entities, value objects etc.

Conclusion

And that’s it! We’ve identified the relevant concepts, models, events and rules for our command business case (click here to see the result in Domain Map). This is information only, it has nothing to do with implementation. But once we have all this, it’s easier to write the code however you like it. The point is, the model stays the same regardless of programming language, framework or programming style. With Domain Map, the focus is on gathering and recording domain knowledge, not writing code.

In the next tutorial, we’re going to model the “List organization members” business case.

DDD Decoded - Domain Relationships Explained

Written on 24 August 2016

One of the most trickiest things to understand in DDD is the domain relationship between 2 concepts. Most of the time when people think relationship they use the programmer mindset and they look for: has-a or is-a or parent-child or (worse) one-to-many etc relationships. Which is valid when you write code but absolutely wrong when you do domain modelling.

What we’re looking for is a Domain Relationship i.e how the Domain sees the relationship from a business point of view or, to be more precise, what is the level of association/partnership between the concepts and what business rules are involved in maintaining the relationship. And usually there are 2 types: strong/always and weak/optional.

Strong (always) domain relationships

This one is the easiest: it simply means that in a given business case, a concept always needs the other. Examples: Customer and Order, Author and Tutorial etc. Contrary to how many devs think, it’s never “Customer has orders”, it’s always an action about an Order which might involve or not the Customer. When the customer places an order, we need to create that order and we’re going to use a specific Order aggregate. But orders don’t just appear from thin air, a customer is always involved so we can’t create a new order without mentioning the customer.

Order concept requires at its creation a Customer so, in our Order model, we need to find the representation of the Customer that will be used by the Order aggregate. In most cases, we only need the simplest model that represents the existence of the Customer, so we’ll use its id. That id is a Value Object and will be a part of the Order aggregate. We don’t care about a Customer aggregate, because we don’t need one for this business case. The Order is the main concept, Customer is secondary.

However, if we are in a “Cancel order” case, there is no Customer-Order relationship to care about (there is an Order-OrderStatus one, though). So, we can cancel the Order without involving/mentioning the Customer, but, as I’ve said, there’s a different relationship to take care of now.

Let’s say that an Order always needs to have a status. It might be tempting to include it as part of the aggregate but… if you look at it like the business does, it makes no sense for this Value Object to be part of it. What makes sense is to consider the Order Status as being a sort of metadata associated with an Order.

When the Order is created, the only place where this relationship should be represented is the resulting OrderCreated event. That’s because a status like “Pending” is implicit, we don’t need any particular input or business constraint for it. But we still need to ‘announce’ that the order has that status, because it is part of the business state change.

Now, when cancelling an order, we need to specify a different status for the order, however because we can have different outcomes based on the existing order status, we need to identify an aggregate just for that. This aggregate involves the Entity Order (via OrderId) and the Value Object OrderStatus and while being a model specific to changing the order status (representing the relationship itself) the resulting change is still associated with the Order concept.

Using Event Sourcing, this means that the OrderCanceled event is considered to be part of the Order and so, the entity’s id is the order’s id. But the aggregate is about the relationship between Order and OrderStatus, it’s not just an Order aggregate. If this sounds weird, I can only say that it will make sense with more experience.

Weak (optional) domain relationships

Basically, 2 concepts can be associated together. Think Post and Category. Both concepts are independent, but they can work together. In the business case “Assign post to category” we need to create the association between them according to business rules. The relationship aggregate will involve both concepts and we get an interesting situation, because we end up with 2 entities but we need only one to act as the aggregate root. Which will it be?

Clearly, we shouldn’t just flip a coin. We need to understand the concepts better as well as the purpose of their association. It turns out that Category exists to organize posts, while a Post really doesn’t care about categories. This relationship is more important (relevant) for the Category so, we select it to act as the aggregate root. This means that in the resulting event, the entity id would be that of the Category while PostId will be just a field.

Being a relationship, even if changes are considered to ‘belong’ to a specific concept, the aggregate is always a model for the relationship.

An interesting example is deleting something. If I soft delete a post, what I have is in fact a new relationship between the Post and Deleted status, the status is never part of the post itself (as a concept) but always some metadata associated with the post. Btw, the status is a Value Object even if its implementation is just an enum.

Another example can be “Pay invoice”. Yet again, a relationship between an Entity (Invoice) and a Value Object (InvoiceStatus) . The status is metadata for the invoice and we need a relationship aggregate to change the status. From the domain point of view, something associated with the Invoice entity has changed.

Conclusion

The hardest part about domain relationships is to properly identify them; many times, especially when an Entity and a Value Object are involved, we can think that the VO is part of the Entity’s aggregate, when in fact it’s a relationship that might need its own aggregate. But with more practice, spotting relationships will become second nature.

DDD Decoded - Modelling with CQS

Written on 22 August 2016

While CQS/CQRS are a bit newer than DDD, they work so well together, one simply has to use it. And I don’t mean CQRS from an architectural point of view, but from a modelling point of view.

Simply put, when we identify a business case we ask ourselves: “Does this case has the intention to change the business state”? We’re not talking about a particular state (objects, tables etc) but about business state as a whole (remember that in real world any change is implicitly persistent). If so, then it’s a command case else it’s a query case (this is CQS at work).

Being in a query case means that you know you won’t be changing anything and that the models will be read-only (this is CQRS, because we end up with specialised read models).

But in a command case, things are a bit different. Many concepts can be involved however, usually only one will be represented by a command model. The other concepts will be represented by read models. We apply CQS for each concept and we end up with specialised command/query models for each, basically CQRS (not to be mistaken for a CQRS architecture, which is a different thing, but based on the same CQS principle).

As an example, let’s say we have the Create invoice business case. Clearly, the business wants to retain the new invoice, so our business case will change the business state. We’ve identified a concept too, the Invoice for which we need to find a relevant representation. However, because we’re creating the invoice, it means we need a command representation i.e an Aggregate.

That’s great, but there’s more concepts involved besides the Invoice. We need to get the products and prices from an Order, we also need to calculate Tax and so on. Each of these concepts have a model as well, but because we don’t need to change the business state for which those concepts are in charge, it means we’re going to have read models representations. So, the Order model will be some read-only structure which contains all the data needed by the invoice. We always try to identify the most relevant model for that specific case, and not a generic model good for everything.

As a generic ‘rule’, consider the business case as being like a bounded context i.e it has a model which makes sense only inside it. Concepts are involved in many business cases, but we work only with their most relevant models and based on the action we need, either write or read, we end up with a command model or a query model for each concept.

So, in a command case, we can find both command and query models of different concepts while in a query case, we’re dealing only with read (query) models. Applying CQS early when modelling helps us to know upfront what types of models we’re looking for, which gives us a bit more clarity and a productivity boost.