Skip to main content

Do your aggregates touch each others' private parts?

Most of current programming languages support access modifiers. I cannot imagine any contemporary language, which would not allow the developer to protect access to classes, methods, fields, or functions. Without them, we would not be able to maintain abstractions and encapsulation.

In Java, there are four modifiers: public, protected, default, and private, in C# there is additonal concept of internal, which reffers to assembly, and in Scala, there are possibilities to fine-tune the scope with scoped private and scoped protected modifiers. Other languages, like C++, or F# for instance, also give us some possibility to modify access. They may vary from language to language, but the idea is the same - to protect parts of the code.
(Actually, in C++ there is also a concept of friend, so that other class can get some private access to another class - hence the comment, that in C++ friends can touch each others' private parts... ;)  )



Looking from the time perspective, I can observe certain regularity. The default modifier (usualy without any keywords) tends to be more public for younger languages. By this I mean, that default access in older languages (like C++) was ment to be private. In Java, which is a little more modern language, when You use no access modifying keyword, You will be able to access that code from the package. And finally, in very young languages like Scala (and in F#, I believe), no access modifying keyword means, that the code is public. Why is that? I do not know. I can only guess, but I personally like the Scala way.

OK, let's go back to Domain-Driven Design...


Aggregate

An Aggregate in DDD, from the code perspective, is a tactical pattern which allows to organize certain functionalities and state, that belong together. It's main responsibility is to protect invariants and hide internal structure. This is a very powerfull modeling tool, but unfortunately the hardest to grasp.

Aggregate is a graph of objects (or functions) tightly connected to each other. This graph is very cohesive and can be accessed from the outside world only through one defined point - it's root. The Aggregate Root is an Entity that acts as this root. It has globally unique identity, and provides API for communicating with the whole Aggregate.

It may contain some Entities and Value Objects. Entities have unique identity inside that Aggregate only and are not supposed to be shared between Aggregates. Value Objects on the other hand, may be shared between Aggregates, or may belong to one particular Aggregate only. I will refer to the latter as Internal Value Objects for the purpose of this blog post.

Since Value Objects, which are shared in the Model with other Aggregates, are not very exciting from this blog post's perspective, we will leave them for now. More interesting are Entities and Aggregate's Internal Value Objects, since they are not visible outside of the graph. Furthermore, if they are so tightly coupled, they should be located in the same package, right?

Entities

Let's face this - huge majority of software is currently being written with Anemic Model - especially in Java world. By Anemic Model, I mean a model, where so called "entity" with a bunch of properties has only getters and setters. Some people even dare to name that encapsulation... I will let that pass... ;) Those "entities", which are actually SQL database records, are mutated back and forth in some Services, or in GUI, or wherever our imagination lead us.

In Domain-Driven Design, Entities mean much more. They have their behavior, which is visible to the whole Aggregate, and they actually need encapsulation. That is why we keep their state private and expose only significant methods to the Aggregate graph. We should not be able to access their methods from the outside of the Aggregate. We do not allow to call them directly.

Or do we?

Protecting Entites

Well... I have not seen any code, where Entities and Internal Value Objects were actually protected from illegal access. Maybe some DDDesigners take care of that, but I didn't spotted any yet... Anyway, we got used to anemic entities so much, that we write this "public" keyword before each class definition, regardless if is supposed to be Aggregate Root or normal Entity.

Without protection, there is no guarantee, that somebody in the future will not construct new instance of some Entity without any Aggregate Root. And it will cause inconsistency. There is also another threat. Somebody may expose Aggregate's state to the GUI. It may not be that dangerous, but again - there is no guarantee, that somebody will not call Entity's methods outside of the Aggregate, causing inconsistency once more.

As I wrote before, all Aggregate's classes (and functions if any) should be located in one package. They are tightly coupled and should be distributed and accessed together. What is more, the only possibility to access the Aggregate is through the Aggregate Root, which, for protection, should be the only public class in the Aggregate's package.

Of course You may have a lot of small Aggregates, which do not have any Entities beyond the Aggregate Roots. In that case, You should not create a package for each of them, but it is definately worth to consider how the model will grow.

The rule of thumb

So, to summarize - to protect Your model (and Yourself), think about the following rule of thumb, which I created for myself:

Each Entity (which is not the Aggregate Root) or Value Object (which is not accessed outside of the Aggregate) should be accessible only by objects inside of the Aggregate itself, therfore their classes should be located in the same package and have package private access modifier.
And how do You protect Your Aggregate's internals?



Comments

  1. I "somewhat" agree. Let me explain: I don't mind exposing entities if the intent is for application services to look them up in the aggregate and then invoke a method on the entity directly. Internally, the entity could have a reference to the aggregate's root which is still responsible for enforcing all invariants. I could also sprinkle one or more role interfaces onto the entity, exposing only allowed command or query methods (CQS). I don't mind exposing entities in general if the idea is to use them transiently. Protecting from developer stupidity may be a concern, but using code and access modifiers is the wrong way to go about it IMO. The reason I feel this way is because I strongly believe in collaboration between objects. I don't want to confine myself to "only use the root" school of thought. I might take a different stance when I'm using a language that promotes a functional and immutable style or when "all" interactions (even changes to related aggregates) are communicated using messaging (but then the coupling or "stuff you need to know how to use this aggregate" shifts towards messaging), especially when collaborators have state required for the given aggregate to protect one of its invariants. In the end, it boils down to making a choice about what works in your environment, so no disrespect. What you're describing seems like a perfect fit for "the only way you're gonna speak to this aggregate is by using a message (and I don't mean the OO interpretation of calling a method by that) and sending it to him", almost like an actor. So there you have it, my nuanced point of view :-)

    ReplyDelete
    Replies
    1. Thanks for Your valuable comment, Yves.
      Sorry for tha late response, but You made me think a little. :)

      I can clearly see, that You can fetch an entity from aggregate and perform some method on it from application layer. I can also agree, that this entity may have referrence to the Aggregate Root and perform some additional action to force AR to take some steps to maintain its invariants.

      But...
      It seems to me, like a violation of the Single Responsibility Principle here... I may be wrong, but it just looks like a smell for me.
      Entity has to do something, and then, totally in addition, has to go back to AR and take care of inforcing invariants, which may even not be aware of.
      I much more prefer "Tell, Don't Ask", and I think, that You should tell AR to do something (react with it's graph internally).

      Regarding those interfaces. Suppose, that the domain really enforces us to expose entities to application layer, as You suggested.
      Then, only those interfaces are public. Entity still should be protected, because You are dealing with the interface. :)

      If You want to use entity transiently (for UI?), You may always return some form of projection (Value Object) representing the state that You are interested in to show.

      As You said - it depends on Your environment - I agree with that one in 100%. :) On the tactical level, You may use whatever patterns You need.
      Maybe sometimes You can violate the SRP, but You have to be extremely careful and You have to know what You are doing. Only big boys can play like that. :)
      That's why I coined this rule of thumb - it is not an axiom. :)

      Again, thanks for Your input.

      Delete
    2. Hi, Piotr,

      Just a quick reply.

      I could argue that changing the interface of the aggregate root each time the behavior of an entity changes (e.g. adding or removing methods, changing signatures and names) is violating that same principle. Direct entity access has gotten a bad rep over the years because it's associated with getters/setters galore and not thinking in terms of aggregates. That doesn't mean it's bad per sé. To me it's just another "flavor" of how you access entities within the aggregate. Basically you trade AR interface 'pollution' for entity coupling. At least, that is what I've learned over the last couple of years. It's still TDA, but with a query method to select the right entity/entities. From a language perspective it may even be clearer to do so (I'm alluding to the query method name here). With the "root only" way of accessing entities, the query criteria method arguments might get mixed with the command criteria method arguments and end up hurting readability of the code (although, in all honesty, some of this could be mitigated by using a bit of lambda magic).
      The argument "but now you know the entities and how they are structured inside the aggregate", although a valid concern, really has to do with how likely that structure is/those entities are to change. If things have stabilized, I see no harm in using entities directly.

      With transient I was rather hinting at "transiently pass it to another aggregate that could do useful, side-effect free stuff with it", not particularly a UI. Both roles interfaces and value object could help complete that mission as well, sure.

      You see, I don't think either one of is right/wrong. It's about having options and being able to motivate why you'd go about it a certain way. Having a bunch of juniors accessing entities directly will probably end with a BBOM where the concept of an aggregate is only a distant memory ;-)
      So for any future readers, take Piotr's advice to heart, practice, practice, and when you've gotten to the point you really "get it", you can experiment with direct entity access. Oh boy, unbuttoning my shirt, coz that just sounded like I'm an expert. I'm not, I just tend to play a lot with different approaches.

      Kind Regards,
      Yves.

      Delete
    3. I see your point, but I think (for now), I will stick to protecting Entities. ;) Especially when things are not stabilized yet.

      When Entity's interface changes it is probably because language has changed. And therefore the conceptual "whole part", which Aggreagte represents, may also change. The argument with interface 'pollution' speaks to me a little, but then, maybe it is worth to concider, if Aggregate's boundaries are not too wide...

      I still consider direct accessing Entities as a smell - role interface, or VO may be something in the middle, that can do the trick.

      As you suggested, there is no right/wrong. I just added another option to protect Aggregate's structure, and to stop and think for a moment. ;)

      Greets,
      Peter

      Delete

Post a Comment