Marlon
Marlon

Reputation: 20314

DDD modeling, referencing child of aggregate root?

I am trying to learn DDD. I am modeling a property management domain and I think I have two contexts (subdomains?): a property management context and a resident context.

Let's say I have an aggregate root Apartment, that holds Floorplans and Units. Each Apartment can have many Floorplans, and each Floorplan can have many Units.

public class Apartment : IAggregateRoot // for clarity
{
    public int Id { get; }
    public Address Address { get; set; }
    public ICollection<Floorplan> Floorplans { get; set; }
}

public class Floorplan
{
    public int Id { get; }
    public int ApartmentId { get; set; }
    public string Name { get; set; }
    public int Bedrooms { get; set; }
    public int Bathrooms { get; set; }
    public ICollection<Unit> Units { get; set; }
}

public class Unit
{
    public int Id { get; }
    public int FloorplanId { get; set; }
    public string Number { get; set; }
}

Let's say in the property management context I now introduce a Resident who gets assigned to a Unit. My Unit and Resident class now looks like this:

public class Unit
{
    public int Id { get; }
    public int FloorplanId { get; set; }
    public string Number { get; set; }
    public ICollection<Resident> Residents { get; set; }
}

public class Resident // in the property management context
{
    public int Id { get; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public void UpdateBalance(...);
}

My question is now if I introduce a Resident in the resident context (that can PayRent() or UpdateProfile(), etc) they must have a 1:1 relationship with Resident in the property management context, but I thought I cannot reference a non-aggregate root entity without going all the way through Apartment because a Resident cannot exist without an Apartment.

Is my understanding of aggregate roots incorrect? Is Resident an aggregate root in both contexts? I'm not sure how that would be modeled.

Upvotes: 1

Views: 569

Answers (2)

V. S.
V. S.

Reputation: 1244

Is Resident an aggregate root in both contexts?

No, it is only the aggregate root in the Resident aggregate, in the Resident context. When you remove an Apartment entity it causes deleting all the other entities in the aggregate inclusive the Resident entity (only in the Property Management context). But when a Resident leaves a unit in the apartment it doesn't require to delete any other entity within the Apartment aggregate.

Is my understanding of aggregate roots incorrect?

No, you are right saying

I cannot reference a non-aggregate root entity without going all the way through

But not only an aggregate root of one aggregate can refer to the root of another aggregate. A non-root entity from one aggregate can reference the aggregate root from another aggregate. Please take a look at the image below.

enter image description here

This image is taken from the Pluralsight course Domain-Driven Design in Practice

For identifying the root entity in aggregate use this principle that is already described in my answer above (the quote is taken from the book Applied Akka Patterns):

Aggregates, and their associated roots, are a tricky concept. It can be difficult to determine what is an aggregate in your system, or more importantly, what is the right aggregate root. Generally, aggregate roots are the top-level pieces of a system. All of your interactions with the system will in one way or another interface with an aggregate root (with a few exceptions). So how do you determine what your aggregate roots are? One simple rule is to consider deletion. If you pick a specific Entity in the system and delete it, does that delete other Entities in the system? If your system consists of people who have addresses, and you delete an address, does it delete other parts of the system? In this case, probably not. On the other hand, if you delete a person from the system, there is a good chance that you don’t need to keep that person’s address around anymore, so in this case, a person might aggregate an address.

Now your question about how it should be modeled:

My question is now if I introduce a Resident in the Resident context (that can PayRent() or UpdateProfile(), etc) they must have a 1:1 relationship with Resident in the Property Management context, but I thought I cannot reference a non-aggregate root entity without going all the way through Apartment because a Resident cannot exist without an Apartment.

  • In my opinion, Resident in the Property Management context should be presented as an entity since it has an ID (e.g. passport, driving license) and the entity must have only those properties from the entity Resident in the Resident context that will be used in the Property Management context.

  • I would suggest reconsidering the name Resident for the context Resident. When you keep information about a person but this person is not currently living in an apartment he\she is not technically Resident of this apartment, it is just a person that is planning\planned to live, currently lives, or lived in the apartment. I think, the better name for what you call the Resident context is Person.

  • For communication between the contexts use integration events (don't confuse them with domain events):

    Domain events versus integration events

    Semantically, domain and integration events are the same thing: notifications about something that just happened. However, their implementation must be different. Domain events are just messages pushed to a domain event dispatcher, which could be implemented as an in-memory mediator based on an IoC container or any other method.

    On the other hand, the purpose of integration events is to propagate committed transactions and updates to additional subsystems, whether they are other microservices, Bounded Contexts or even external applications. Hence, they should occur only if the entity is successfully persisted, otherwise it's as if the entire operation never happened.

    As mentioned before, integration events must be based on asynchronous communication between multiple microservices (other Bounded Contexts) or even external systems/applications.

    Thus, the event bus interface needs some infrastructure that allows inter-process and distributed communication between potentially remote services. It can be based on a commercial service bus, queues, a shared database used as a mailbox, or any other distributed and ideally push based messaging system.

    Domain events: design and implementation

I have noticed that this part of your question:

I am trying to learn DDD. I am modeling a property management domain and I think I have two contexts (subdomains?)

sounds like you are not sure about the meaning of terms context and subdomain. Please take a look at the image below; it is quite self-explanatory:

enter image description here

This image is taken from the Pluralsight course Modern Software Architecture: Domain Models, CQRS, and Event Sourcing

A sub-domain represents a part of a problem, whereas a bounded context represents a part of a solution that solves the problem. Each sub-domain usually covered by one bounded context. In some cases, one sub-domain may correspond to more than one bounded context, e.g. when you have a sub-domain with old requirements and a bounded context that already implemented these requirements, and later you have new requirements and you don't want to change the existing bounded context for some reason, then you may introduce the new bounded context for these new requirements. This new context should be separated via anti-corruption layer from the old one.

Upvotes: 1

Louis
Louis

Reputation: 1241

My question is now if I introduce a Resident in the resident context (that can PayRent() or UpdateProfile(), etc) they must have a 1:1 relationship with Resident in the property management context, but I thought I cannot reference a non-aggregate root entity without going all the way through Apartment because a Resident cannot exist without an Apartment.

It depends on your architecture. For the most part, it makes sense to me to have an event outside of your domains that'll be handled by both contexts (flowing through the application service down to the aggregates) and will react by "creating" the appropriate actors (a resident and property managent client?). Ensuring 1:1 here would imply ensuring that all events are handled and handling failures graciously (This sounds like a fun excercise).

All contexts will have a boundary. The boundary will serve as the contexts "interface" to consumers. Any communication between contexts should go through this "interface". As an example: If your contexts is implemented as a microservice (a REST API), you should only communicate to it by sending HTTP REST commands. This isolation keeps the contexts clean. That is the purpose of having contexts.

Upvotes: 1

Related Questions