I have begun using domain driven design principles but are currently stuck with a specific problem.
I have an Aggregate which has multiple layers of nested child entities as shown below:
public class Aggregate: Entity<AggregateId>, IAggregateRoot {
private readonly List<ChildOne> childOnes;
}
public class ChildOne: Entity<ChildOneId> {
public string ChildOneValue1;
public string ChildOneValue2;
public string ChildOneValue3;
private readonly List<ChildTwo> childTwos;
}
public class ChildTwo: Entity<ChildTwoId> {
public string ChildTwoValue1;
public string ChildTwoValue2;
public string ChildTwoValue3;
}
In a domain service I need to access all values of ChildOne and ChildTwo including their ids.
public interface IDomainService {
public IEnumerable<INotification> Analyze(Aggregate aggregate);
}
However, I cannot return the entities as they are since this would violate the immutability principle.
This got me thinking that my domain model probably might not be optimal, but I cannot see how this can be different since the nested entities never can exist without the respective parent.
Another approach could be to have a single value object which holds all values of a given entity and then return this value object instead of the entity. But then a deep mapping needs to be performed since the domain service needs access to the values of all nested entities.
Any suggestions on to how to approach this?
Any suggestions on to how to approach this?
There are a couple of possibilities.
One is to invert your proposed API - design the domain service to accept immutable values as arguments, and then pass the instance of that service to the Aggregate, rather than the other way around.
public interface IDomainService {
public IEnumerable<INotification> Analyze(IEnumerable<DomainValue> values);
}
If you recall the Visitor pattern from the Gang of Four book, it is a very similar idea.
In some cases, an in-memory state machine might be a reasonable alternative to a stateless domain service. In that case, the API might look like
public interface IDomainService {
public void OnAnalyze(DomainValue value);
public IEnumerable<INotification> Notifications();
}
Another possibility is to design the Aggregate to act as a Collection
public class Aggregate {
// ...
public IEnumerable<DomainValue> YourCleverNameHere();
}
For use cases where you are not changing the state of the Aggregate, just leave the aggregate out of it, and work directly with an immutable in memory representation of the current state.
However, I cannot return the entities as they are since this would violate the immutability principle.
The entites don't need to be immutable. It's only value object that are immutable in tactical design of domain driven design.
As far as entities are aggregates are concerned, you should be able to get/save an aggregate from the repository only using the aggregate root. You should not be able to get/save an entity that is not an aggregate root.
Related
Supose I have an order and it can be in an state.
Order it would be the root entity, and State another root entity in the same bundary context than the order.
I was thinking that State it should be an entity too because it would be in this way:
State
{
long Id,
string State;
bool AllowModifyOrder;
bool AllowAceptorder;
//another properties that define what is possible to do in the state.
}
So I think it is needed that the State it is another entity, not a value object. But I am not sure if this it would be the best option.
Another doubt that I have is that the order need a property to point to the state, but not to the class, but to the ID, because I have read that an entity it is better to use the ID instead of the object, to avoid try to access or modify a root entity from another. But in this case, when I need some data of the state, for example if it can be accepted or if it can be modify. How could I do that if I only have the ID of the state?
Thanks.
Your State has to be a property of an Order because your Ubiquitous Language says "An Order can be in a state". You are tieing yourself too closely to the Database Model. That's why you have Id's and properties that look like Columns in your class.
If an Order has a State and there's a finite amount of States, then your State can be modeled as an Enum. Take this C# code for an example:
public Enum OrderState {
Draft,
Open,
Closed
}
public class Order {
OrderState State { get; private set; }
}
Your Repositories and Factories will take care of translating what you have in your Database and Inputs into Domain Classes like Order.
Let me preface this by apologising if this was asked before (I could not find a similar question when doing a quick search).
The DDD pattern is centered around defining and isolating aggregates to split the business complexity into a more manageable chunks, and it is strictly forbidden for an aggregate to hold any kind of relation to other aggregates. The references to the other aggregates are instead to be stored as ids that can then be used to fetch the other aggregates on demand. Therefore a single aggregate would only contain its properties, value objects and references to other aggregates.
To use a common example (User, Order) it would look as such:
public class User {
private Long id;
private List<Long> orders;
}
and
public class Order {
private Long id;
private Long userId;
}
However I have seen multiple sources use another layer of encapsulation for the aggregate references, turning them from the property types (as defined in the example above) into value objects like shown below:
public class User {
private Long id;
private List<OrderId> orders;
}
and
public class Order {
private Long id;
private UserId userId;
}
I am rather new to DDD so I want to understand the benefit of doing so when working with non-composite ids.
On the first glance I see a lot of pretty obvious drawbacks (or such they seem to me!), like explosion in quantity of common base types, serialization issues, extra complexity when working with the code and accessing the values stored within these holders, however I am sure that it would not be done so without a very good reason that I am overlooking somewhere.
Any comments, thoughts or feedback would be very welcome!
This is a domain abstraction
private UserId userId;
This is a data structure
private Long userId;
Your domain logic (probably) doesn't care, or need to care, about the underlying data structure that supports representations of UserId, or how they are stored in the database, or any of that nonsense.
The broad term is "information hiding" -- creating firewalls around decisions such that the decision can be changed without that change cascading into the rest of the system. See Parnas 1971.
There are some mistake detection benefits as well. Consider
todays_order.userId + yesterdays_order.userId
That's utter nonsense code; adding two identifiers together doesn't do anything useful. But adding to Long values together is a perfectly normal thing to do in other contexts, and the compiler isn't going to catch this mistake.
recindOrder(orderId, userId)
Did you catch the bug? I've got the arguments in the wrong order! When the method signature is
recindOrder(Long userId, Long orderId)
The machine can't help me catch the problem, because I haven't given it the hints that it needs to look beyond the data structures.
There is also a theory that by providing an explicit representation of the domain value, that code attracts other related functions that otherwise might not find a home -- in effect, it improves the coherence of your design.
(In my experience, that's less true of semantically opaque types like identifiers than it is for numerical abstractions like money. However, if you have some identifiers that are reserved, then the identifier type becomes a convenient place to document the reservation.)
I was wondering whether it would be considered bad practice to use an aggregate identifier across a service in another (extensipn) aggregate which shares that they are both revolving about the same identifiable entity.
The problem I am currently having is that we want to split some logic (bounded context if you so will) into a different service as the one originally creating the aggregate.
In general, this seems to work, as when I send a Command within the second service, it is picked up and updates its state. As I can use EventSourcingHandler to also use Events created in the other service to manipulate its state, I get state information from a source applied by the first services aggregate.
I was worried that the snapshot mechanism would work against me, but apparently it is smart enough to store snapshots separately as long as I make sure the aggregate "type" name is not the same.
So far, so good, the only thing that's a smell for me is that the second aggregate does not have (needs) an initial constructor CommandHandler, as the creation is done in the first aggregate.
So, am I going against the way axon framework intends aggregates to be used, or is this a viable use case?
#Aggregate
#Getter
#NoArgsConstructor
public class Foo {
#AggregateIdentifier
private String fooIdentifier;
#CommandHandler
public Foo(CreateFooCommand command) {
apply(FooCreatedEvent.builder()
.fooIdentifier(command.getFooIdentifier())
.build());
}
#EventSourcingHandler
public void on(FooCreatedEvent event) {
this.fooIdentifier = event.getFooIdentifier();
}
}
#Aggregate
#Getter
#NoArgsConstructor
public class Bar {
#AggregateIdentifier
private String fooIdentifier;
private String barProperty;
#CommandHandler
public void on(UpdateBarCommand command) {
apply(BarUpdatedEvent.builder()
.fooIdentifier(this.fooIdentifier)
.barProperty(command.getBarProperty())
.build());
}
#EventSourcingHandler
public void on(FooCreatedEvent event) {
this.fooIdentifier = event.getFooIdentifier();
}
#EventSourcingHandler
public void on(BarUpdatedEvent event) {
this.barProperty = event.getBarProperty();
}
}
The case for why I tried to split is that we wanted to separate the base logic (creation of the aggregate, in this case a vehicle) from the logic that happens and is handled in a different bounded context and separate microservice (transfers from and to a construction site). Since I cannot publish a creation event (CommandHandler in the constructor, sequence 0) for the same aggregate identifier but different aggregate type twice, I could not separate the two states completely.
So my only options right now would be what I presented above, or use the creation of the second aggregate to set a different aggregateId, but also add internally the aggregateId of the first aggregate to allow for events to be published with the aggregateId information of the first as a reference Id. To make this work I would have to keep a projection to map back and forth between the two identifiers, which also does not look too good.
Thanks in advance,
Lars Karschen
Very interesting solution you've come up with Lars. Cannot say I have ever split the Aggregate logic in such a manor that one service creates it and another loads the same events to recreate that state in it's own form.
So, am I going against the way axon framework intends aggregates to be used, or is this a viable use case?
To be honest, I don't think this would be the intended usage. Not so much because of Axon, but more because of the term Bounded Context you are using. Between contexts, you should share very consciously, as terms (the ubiquitous language) differs per context. Your events are essentially part of that languages, so sharing the entirety of an aggregate's stream with another service would not be something I'd suggest normally.
Whether these services you are talking about truly belong to distinct Bounded Contexts is not something I can deduce right now, as I am not your domain expert. If they do belong to the same context, sharing the events is perfectly fine. Then still I wouldn't recreate a different aggregate based on the same events. So, let me add another concept which might help.
What I take from your description, is that you have something called a Vehicle aggregate which transitions different states. Wouldn't a Polymorphic Aggregate be the solution you are looking for? That way you can have a parent Vehicle aggregate with all the basics, and more specific implementations when necessary? Still, this might not fit your solution completely, something I am uncertain about given your description.
So, I am going to add a third pointer which I think is valuable to highlight:
Since I cannot publish a creation event (CommandHandler in the constructor, sequence 0) for the same aggregate identifier but different aggregate type twice, I could not separate the two states completely.
This line suggests you want to reuse the Aggregate Identifier between different Aggregates, something which comes back in the question's title too. As you've noted, [aggregate identifier , sequence number] pairs need to be unique. Hence, reusing an aggregate identifier for a different type of aggregate is not an option. Know however that Axon will use the toString method of your aggregate identifier class to fill in the aggregate identifier field. If you would thus adjust the toString() method to include the aggregate type, you'd be able to keep the uniqueness requirement and still reuse your aggregate identifier.
For example, the toString method of a VehicleId class containing a UUID would normally output this:
684ec9f4-b9f8-11ea-b3de-0242ac130004
But if you change the toString to include the aggregate type, you would get this:
VehichleId[684ec9f4-b9f8-11ea-b3de-0242ac130004]
Concluding, I think there are three main points I'd like to share:
Axon Framework did not intent to reuse Aggregate Streams to recreate distinct Aggregate types.
Polymoprhic Aggregates might be a means to resolve the scenario you have.
The [aggregateId, seqNo] uniqueness requirement can reuse an aggregateId as long is the toString method would append/prepend the aggregate type to the result.
I hope this helps you on your journey Lars. Please let me know of you feel something is missing or if I didn't grasp your question correctly.
I am practicing Domain-Driven Design so why not build a demo product catalog project? Apparently Product is the Core Domain here, but since I like to make the project more interesting, I would love to support nested Category hierarchy. In other words, a Category could have many child Category.
Moreover, I would want to separate Category from Product domain and make it its own Supporting Domain.
Question: Marking Category as AggregateRoot doesn't sound right to me. A Category could have many child Category, which are also AggregateRoots?!! How can I go about modeling this? Nested product category is pretty common in E-Commerce real life situation.
namespace DL.Demo.Domain.Shared
public abstract class ValueObjectBase<T> : IEquatable<T>
where T : ValueObjectBase<T>
{
public abstract bool Equals(T other);
public abstract override bool Equals(object obj);
public abstract override int GetHashCode();
}
public abstract class EntityBase : IEquatable<EntityBase>
{
public Guid Id { get; private set; }
protected EntityBase()
{
this.Id = Guid.NewGuid();
}
// Some Object overrides
}
And I actually have AggregateRoot inherents from Entity because I guess only an Entity could be an AggregateRoot?
public abstract class AggregateRoot : EntityBase
{
}
namespace DL.Demo.Domain.Catalog
public class Category : AggregateRoot
{
public string Name { get; private set; }
public Guid? ParentCategoryId { get; private set; }
public CategoryStatus CategoryStatus { get; private set; }
}
Having a nested list of AggregateRoot just doesn't sound right to me. If you don't mark the Category as the AggregateRoot, how would you go about modeling this?
I am new to DDD and all other related cool stuff like Domain Events, Event Sourcing, etc. I will be appreciated if somebody who had experience can tell me if I am going to the right way.
I am new to DDD and all other related cool stuff like Domain Events, Event Sourcing, etc. I will be appreciated if somebody who had experience can tell me if I am going to the right way.
You are on the right way.
Category should be an Aggregate root, with a reference to parent category by it's ID and this is very good.
Nested categories are a good candidate for event-sourcing, even if there are no special invariants to protect because of the different modes that this hierarchy could be projected in the Read models. You are not limited in any way on that representation, although the Aggregate is straight-forward. In every used Read model you could implement them differently as:
Model Tree Structures with Parent References
Model Tree Structures with Child References
Model Tree Structures with an Array of Ancestors
Model Tree Structures with Materialized Paths
Model Tree Structures with Nested Sets
See more here about implementing tree structures (this link points to MongoDB but that is not relevant).
The Category Aggregate just emits simple events as ACategoryWasCreated, ACategoryHasMovedToOtherParent and so on and the Read models just adapt to reflect those events.
I've implemented a tree structure like this and the queries on the read-side (the query side) are very very fast. You could select the products in a category and all child categories with no joins. Or you could build a category path, again, with no joins.
The key to defining an aggregate is to define first a transactional boundary. Outside of an aggregate boundary consistency is eventual - achieved by reacting to the domain events emitted by an aggregate.
Aggregate can hold another aggregate ID (Value Object) as a reference, however, is not responsible to be transactionally consistent with another aggregate.
So, the main question - Is your tree transactionally consistent? If yes, linked list won't scale well. You have to model it differently.
Modeling is context specific and is not cookie-cutter exercise. Maybe your category is just a value object that could be modeled as a path. Hard to say without broader context.
If you want to have a category tree, then the tree itself should probably be your aggregate root (I see you were coming to this conclusion on your own in the comments already). And this would have functions to add or remove children and so on.
And yes, with very large trees you could probably gain a lot of performance in having a read-only projection of your tree in for instance json format (stored in MongoDb, cache, file or whatever). Especially considering how such a category tree is typically updated only a tiny fraction of how often it is read, you could also easily get away with always just maintaining that json and foregoing a normalised database table tree altogether.
I have a problem with regards to entity design. As I've read, when you design a DDD entity, the constructor should contain the values needed for an entity to "exist". For example, in the domain I am working on, a Class entity cannot exist without a Section and a Level:
public class Class
{
public Class(short id, string section, Level level)
{
ID = id;
Section = section;
Level = level;
}
//
// Properties
//
public short ID { get; private set; }
public string Section { get; private set; }
public Level Level { get; private set; }
//
// Methods
//
public static IList<Class> GetClassesByTeacher(short teacherID)
{
List<Class> classes = new List<Class>();
classes.Add(new Class(1, "a", null));
classes.Add(new Class(2, "b", null));
classes.Add(new Class(3, "c", null));
return classes;
}
}
Here Level is also an entity. As I am not yet finished in the design, Level's contructor might also contain an entity SchoolYear. What bothers me is for me to call GetClassesByTeacher method, I need to instantiate a Class along with other entities (Level, and also SchoolYear, needed in Level's constructor).
Is this the correct? I think it's bothersome when I just want to call the method. Are there other ways? I considered making it static but others said testability would suffer. I'm not sure if CQRS is one of the solution for what I want to do as I haven't yet read too much about it, but if it is, are there any other techniques aside from CQRS I can employ, or is it when going DDD, this is how it really is? Or is my entity design incorrect?
You should reconsider you domain model, as you actually say it seems there something wrong.
I may refer to Single Responsibility Principle (SRP) in which any class should have one one reason to change. In your example, what happens if we added a new field to 'Class', we will modify 'Class' it self and is right, but what happens if we decide that the listing should be on reversal order, or if you need a new type of list taking into account only interim teachers... you should change 'Class' but 'Class' has nothing to do with listings.
To answer you question, you might take a look at Repository pattern. Repositories are where you could ask for these type of listings.
I might split Class into two:
one for 'Class' model
other for 'Class' repositories
In summary:
public class Class
{
public Class(short id, string section, Level level)
{
ID = id;
Section = section;
Level = level;
}
//
// Properties
//
public short ID { get; private set; }
public string Section { get; private set; }
public Level Level { get; private set; }
}
public class ClassRepository
{
private IList<Class> contents;
//
// Methods
//
public IList<Class> GetClassesByTeacher(short teacherID)
{
List<Class> classes = new List<Class>();
for (Class elem: contents) {
if (elem.getTeacher().equals(teacherID) {
classes.Add(elem);
}
}
return classes;
}
}
repository = new ClassRepository;
level1 = new Level();
repository.Save(new Class(1, "a", level1));
repository.Save(new Class(2, "b", level1));
repository.Save(new Class(3, "c", level1));
result = repository.GetClassesByTeacher(1);
There are other details like using a ClassRepositoryInterface and implement with an InMemoryClassRepository, you will miss also Teacher information in class, as long as one one teacher drives the class, if not you might change how to filter by teacher, etc, etc.
I'm not a Java developer and the code will not compile but I expect you get the idea.
The GetClassesByTeacher method does indeed belong in some kind of repository or service class. Your Class entity is meant to represent an instance of that thing in the real world. An entity is not meant to provide instances (say, from some underlying persistence) - they are only meant to represent them. A ClassRepository would be a way to provide instances of the Class entity into your domain.
You also mentioned that Class cannot exist without a Level. You are speaking about aggregates here. There is a lot of DDD material online with regards to designing aggregates. Here are a couple:
DDD: Aggregates and Aggregate Roots
DDD: The Aggregate And Aggregate Root Explained
EDIT:
When an entity needs another entity to exist, should it always be an
aggregate?
No, just because entity A depends on entity B's existence, it doesn't mean that entity A needs to belong to entity B's aggregate.
As now I have a model in which many entities (like Class, Level,
Subject, Adviser, Teacher, etc.) exists only in a certain SchoolYear
(an entity). Is it OK for an aggregate to get that large?
An aggregate this large could result in performance and consistency issues.
Why performance? You could be loading a huge graph of data in memory for some aggregate root. You most likely only need to work on a fraction of that aggregate for whatever unit of work is occurring and not all the entities involved.
What consistency issues? The larger the aggregate, the more likely data can be changed in another part of the system while it has been retrieved into memory. When saving, data loss can occur.
Vaughn Vernon covers exactly these problems in his three-part Effective Aggregate Design series. It's a bit daunting if you aren't familiar with DDD lingo, but I highly recommend this read!
Also, by what you said, the Entity should not use the repository. If
some business logic in the Entity needs database access for it to be
processed, should it directly use DAL?
Entities should not know anything about repositories or services (or anything else, for that matter). They should not have any dependency on any other 'layer' in the system. Everything can know about your entities, but your entities shouldn't know about anything else. Provide me with an example of why you would want an entity to call on a repository and maybe I can provide a better answer.