Recently I came across a post from Udi Dahan talking about how Aggregates should not be creating out of nothing but rather aggregates should be created from other aggregates as that captures the domain better. Seems somewhat logical to me but I seem to be struggling with how to implement this and understanding where event(s) should be raised.
I'm creating a leaderboard application. In my domain, only Administrators can create a leaderboard. I'm trying to see how to best capture this rule in a clean way so that the domain can be highlighted while ensuring invariances are respected. So far, I have decided that an Administrator and Leaderboard are going to be Aggregate roots (please advise if you think otherwise).
Below is what I have so far in Typescript.
const User extends AggregateRoot { /*whatever*/}
class Adminstrator extends User {
// blah blah
createLeaderboard(name) {
return Leaderboard.Create(name);
}
}
class Leaderboard extends AggregateRoot {
public id;
public name;
private create(id: LeaderboardId, name: LeaderboardName) {
const leaderboardCreatedEvent = new LeaderboardCreatedEvent(id, name);
this.applyNewEvent(leaderboardCreatedEvent);
}
static Create(id: LeaderboardId, name: LeaderboardName): Leaderboard {
const params: ConstructorCreateParams = {
type: ConstructorType.Create,
id,
name
}
return new Leaderboard(params);
}
private constructor(params) {
if (params.type === ConstructorType.Create) {
this.create(params.id, params.name);
} else {
this.load(params.events);
}
}
}
class CreateLeaderboardCommandHandler {
constructor(adminstratorRepo, leaderboardRepo)
execute(command) {
const admin = this.adminRepo.get(command.userId);
const leaderboard = admin.createLeaderboard(command.name)
leaderboardRepo.save(leaderboard);
}
}
Few questions about the implementation I put together:
- Where is the best place to enforce the fact that only Administrators can create leaderboards. Is it fine the way I did it. Would it be in the constructor of the Leaderboard?
- In the above example, the admin aggregate is doing something (i.e. creating a leaderboard), but we persist the Leaderboard event (i.e. LeaderboardCreatedEvent). Is that fine. Is this an anti-pattern? I guess I'm wondering whether or not the entity (Administrator) that performs an action (Creating a leaderboard) should be the one that raises an event.
Your input is very much appreciated here.
Creation patterns are weird.
I believe the key thing to recognize is that you are here dealing with a branch; you want one outcome when your data model looks like this, and a different outcome when your data model looks like that.
Once you recognize that there is a branch, you get to think about how obvious you want that branch to be in the code. That might mean having a happy path and an implicit exception path, it might mean having two callbacks, it might mean returning an "Or type", ....
A non-robust approach to illustrate the idea is to imagine the admin repo returning a promise
Another possibility is that the branch belongs further upstream - we run some protocol to check that the source of the command is authorized before we pass that information to the domain model for processing. (Even in that case, you might want the "redundant" check because defensive programming? It's trade offs all the way down.)
In the general case, it's suspect -- it may be fine in your context.
There are two candidate problems:
ending up with a corrupted data model because you "followed the rules" of modeling domains with entities, when those rules were developed with in idealized conditions that you aren't able to reproduce in your environment (no lost messages, no duplicate messages, no concurrent writes...)
having a robust implementation with an unsatisfactory design that is also expensive to change safely (because the module boundaries are badly chosen, or the code obscures what is really going on, or because there are too many different variations of the same core instructions....)