6

For the purpose of separating different functionality into different classes, I have two following Aggregates:

  1. ActiveEmployee

    • AssignTask ()
    • ReassignManager ()
    • Deactivate (string reason)
  2. InactiveEmployee

    • GetReasonForDeactivation ()
    • Reinstate ()

Both aggregates share the same root, dbo.Employee.

EmployeeID servers as a primary key for both Aggregates.

The repository instantiates the first one, or the second one based on a boolean flag dbo.Employee.IsActive.

Since I follow the rule that only one aggregate can be modified in one transaction, I will never encounter a situation where I load both aggregates with same ID, ending up with InactiveEmployee with ID 3 and ActiveEmployee with ID 3 in same transaction.

Is this good DDD? Does DDD allow for having two aggregates with same root? Can we differentiate Aggregate types based on a flag?

UPDATE 1:

When I said that both aggregates share the same root, what I meant was that both Aggregates represent a row in dbo.Employee table.

In my understanding, a root of the Aggregate is the entity which is uniquely defined in our domain. It does not mean that that entity has to exist as a real class, it's identity is what matters. Every other Aggregate that wants to reference Inactive/ActiveEmployee will hold a reference to the EmployeeID, not the reference to object instance.

5
  • 1
    Citing DDD book An AGGREGATE is a cluster of associated objects that we treat as a unit for the purpose of data changes. It is not clear for me what objects both aggregates comprise. May 23, 2015 at 20:52
  • What do you actually gain by using inheritance? Are you confusing aggregate root and object hierarchy root (parent)? From what you have shown I don't see you need inheritance May 23, 2015 at 21:25
  • @EsbenSkovPedersen I'm not using inheritance, these classes do not share the same base class, nor do they inherit from one another.
    – omittones
    May 24, 2015 at 11:19
  • I don't follow then. what do you mean "they share the same root"? May 24, 2015 at 17:56
  • @Esben The root entity in both cases is Employee class. Both of them are persisted to the same database table.
    – omittones
    May 24, 2015 at 17:59

4 Answers 4

11

You need to understand that DDD is not about primary keys, rows or tables - these are just means to implement it.

Aggregate root is usually implemented as a class, because you are expected to access all the functionality and data of the aggregate through the root. It therefore needs to have some behavior, something which primary key of the table can't have. Primary benefit of this is the encapsulation of aggregates - its logic is completely contained inside the aggregate and doesn't leak outside which greatly reduces the coupling (and as a consequence complexity) of the application.

It follows that each aggregate has its own aggregate root, so two aggregates can't have the same root (if they have, there is effectively just one aggregate).

In your case, you probably want to have two independent aggregates (ActiveEmployee, InactiveEmployee) which are backed by the same table (which is fine because it's totally out of DDD's scope). But then remember that there's actually no dbo.Employee entity in your domain model, it's just a table on a lower (persistence) layer.

7
  • Ok, so what is the difference between Aggregate and Aggregate Root? In my case, what is the aggregate and what is the root?
    – omittones
    May 24, 2015 at 13:50
  • 1
    @omittones Aggregate root is one part (and the interface) of the aggregate. Aggregate usually contains multiple domain objects and behaviors which are "owned" by the aggregate (root). In you case, ActiveEmployee is both aggregate and the aggregate root - but the aggregate can further contain more domain objects - like EmployeePreferences, Address, TaskAssignment etc. - these are owned by the aggregate and accessed through the aggregate root.
    – qbd
    May 24, 2015 at 14:07
  • 2
    That is a bit different interpretation than what Evans proposed in his book. Quote: "An AGGREGATE is a cluster of associated objects that we treat as a unit for the purpose of data changes. Each AGGREGATE has a root and a boundary. The boundary defines what is inside the AGGREGATE. The root is a single, specific ENTITY contained in the AGGREGATE."
    – omittones
    May 24, 2015 at 14:26
  • My interpretation was that Aggregate is the interface that you use to access and modify data, and that Aggregate operates on the Entity (and child Entities) contained within that Aggregate, and that Aggregate identity is tied to the root Entity identity, making that Entity a Aggregate's root. Does this make sense? I want to clarify the concepts as much as possible, in order to better communicate my questions.
    – omittones
    May 24, 2015 at 14:26
  • Plus, any reference to the Aggregate in the outside of the Aggregate must be made using that Aggregates Root, and that reference must not be a reference to the object, but to the key that identifies that object.
    – omittones
    May 24, 2015 at 14:29
3

If Employee is an aggregate root, it means that all the outside objects can reference only the Employee (and not the ActiveEmployee/InactiveEmployee).

Any references from outside the aggregate should only go to the aggregate root. DDD aggregate by Martin Fowler

I don't think that's your intent.
I think ActiveEmployee and InactiveEmployee are both independent aggregate roots in your example.
These two are completely different. They expose different APIs and require different handling. They do share some information (Employee table), but that aside they have no interaction with one another. They cannot substitute one another and their APIs can't be invoked without checking the Employee status first

This situation looks a lot like violation of the Liskov Substitution principle to me.

4
  • I don't think it's a violation because they don't inherit from the same class. There is no BaseEmployee class. You cannot pass InactiveEmployee to a method expecting ActiveEmployee. When I say Employee is Aggregate Root, in my understanding that means both Active and InactiveEmployee have EmployeeID as their primary key. Any object that wishes to hold a reference to Active/InactiveEmployee will hold only EmployeeID, not the reference to entire object.
    – omittones
    May 24, 2015 at 11:17
  • That's good to know. But, aggregate root is an object, not a property. All internal objects (Active/Inactive employee in your case) should be accessible thorough the root. It looks like you're redefining the definition of the aggregate root, thus making the question invalid
    – DanielS
    May 24, 2015 at 12:35
  • So you're saying that both InactiveEmployee and ActiveEmployee are roots, and that it's ok they modify the same table, because they are completely different implementation and can't exist at the same time in the same transaction. To what would outside objects hold a reference to?
    – omittones
    May 24, 2015 at 12:55
  • Yes. I think it's ok.
    – DanielS
    May 24, 2015 at 12:58
1

So, I think what the OP has done is create 2 Aggregate Roots called ActiveCustomer and InactiveCustomer, and in those has added a property of type Customer. This is an entity, which represents a Customer in his system.

This is where I think the confusion/issue has arisen. Effectively this has made the aggregate root a wrapper around the entity, but it should be much more.

You need to stop thinking about the database, tables, anything to do with persistence. That stuff doesn't exist in your domain. Instead, just see your ActiveCustomer and InactiveCustomer. They may have many of the same properties, but they have different behavior. So build that behavior.

When it comes to persistence, what I do is have an Infrastructure project which contains my DB access functionality, whether it be EF, NHIbernate etc. Within that you can map your objects to tables. What I have started to do is create an internal data state class within my Aggregates which is what EF maps to. So, you might have something like CustomerDataState which has all your public getters and setters and identity on it. You use your EF or NHibernate to populate that state object, which in turn sets up your aggregate. That state class isn't exposed through your aggregate, so as far as the users of the domain model are concerned they are just working with your ActiveCustomer or InactiveCustomer.

So your Active/InactiveCustomer aggregate is loaded up from or rehydrated from a CustomerDataState which can be loaded from some persistence medium.

I find the best way to work with DDD is to ignore the database, forget about it, pretend it doesn't exist. As soon as you start thinking about how your Aggregate will be persisted you have lost your way. So as soon as a thought about rows or tables pops into your head, give yourself a shake and step back.

2
  • The Q&A format isn't one for discussion. Posing additional questions or asking for opinions don't belong in the answer.
    – user40980
    Feb 1, 2016 at 14:09
  • Apologies. I simply wanted to add to the answer (but as I didn't have over 50 reputation) I had to add it as a new answer. My intro text was just there to explain why I am answering a post which is over 8 months old. Feb 1, 2016 at 15:01
1

My opinion, you can have multiple Aggregate root who actually map the same persistence record (e.g. Rows in a table). But as the other replies have pointed out, you'd better avoid inheritence.

For you specific case, I think there are a few DDD principals violated:

  1. A single domain should have its boundary decided by the business natural instead of the technology concerns. Employee is obviously an Aggregate root for its problem domain, I don't think there are two separately Roles in a company dedicate for Active Employee manangement and Inactive Employee management. This design mixed the Entity and the behavior can be applied on an Entity, I can smell database driven design.

  2. This design violated the layered architecture promoted by DDD. The layered architecture has the principal that higher level layer should only depends on the interface exposed by lower level layers and has zero knowledge about the internal state of the lower layer. But the class ActiveEmployee and InactiveEmployee obviously leak the SQL query logic into domain layer and even worse it will leak into application layer by using these classes in it. Make the code tight coupled into these two states. It leads to two major issues:

    • Think about if you want to add one new state InTransition for an Employee to denote the special period between an Active Employee and becoming an Inactive Employee. You will have a new Emplopee subclass InTransitionEmployee and you have to change your code from UI, Application layer, domain layer to persistence layer.
    • You hard coded the state into the class declaration. What will happend when you call an InactiveEmployee instance's Reinstate() method to make it active again? The instance of InactiveEmployee is obviously inconsistent with the real state Active. It will definitely leads to hard to detect runtime bugs.

By following the DDD principal, I think your case can be refactored into:

  1. A single Employee class as the Aggregate root. Active, Inactive is just two values of the Employe.state property.

  2. A Employee Repository which provide the Repo method findActiveEmployees() and findInactiveEmployees(). But we may not need these two specific methods and a generic query method is enough.

  3. Refactor the methods in your Employee class design to extract them to services class, e.g.

    • assignTask() should be a service function of somethign like TaskService:
    public class TaskService {
        public void assignTask(Task task, Employee employee) {
            if(!employee.isActive()) threw new UnsupportedOperationExeption("You cannot assign a task to an INACTIVE employee.");
            ......
        }
    }
    
    • reassignManager() should be extracted into OrganizationService:
    public class OrganizationService {
         public void reassignManager(Employee employee, Employee manager) {
             if(!employee.isActive()) threw new UnsupportedOperationExeption("You cannot reassign the manager of an INACTIVE employee.");
             if(!manager.isActive()) threw new UnsupportedOperationExeption("You cannot assign an INACTIVE employee to be the manager of other employees.");
    
             employee.setManager(manager);
             ......
         }
    }
    

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.