Referencing external entities or extending the domain model

Hi,

in my application are some entities that are (in my current concept) not part of the domain which is implemented as cqrs. These entities are managed in an sql table via simple CRUD-interface but are referenced by my domain objects. So I wonder how to deal with them:

  • extend my domain model and add these entities. Then I’d mix crud and crqs in one domain
  • leaving them in a separate model. But then I have to somehow ensure the integrity of the reference in both directions.

Kind regards,
Michael.

Keep these entities as CRUD. If they’re only reference data, it’s probably a waste of effort to use event sourcing.
As for the rest, work with them as with any other entity. Denormalize when necessary in your query models, and use the ID’s to reference them in the command model.

Cheers,

Allard

How about maintaining integrity? E.g. one of my external entities is “customer”. Customers are a CRUD but the domain aggregate roots belongs to exactly one customer (customer is an attribute of them). So if at least one entity references a customer, I cannot (or should not) delete them anymore.

One way to enforce that is by checking the query tables before you delete. You could also maintain a table of ‘taken’ id’s and use a foreign key to prevent deletion.

Something to consider as well is that ‘deleting doesn’t exist’. Instead of deleting an entry, mark it as unavailable. In the end, you event sourcing history still refers to it.

This also means tou should be careful when replaying. You might encounter references that don’t exist (anymore).

Cheers,

Allard

@Allard, I think about modelling the entities as CQRS as well. Althought they are CRUD, the have influence to the Root Aggregates.

My Resaon (Example):
RootAggregate “Issue” belongs to a “Category” and a “Project”. “Category” and “Project” are basically “CRUD” entities without domain logic.

An “Issue” can be assigned to a “Project”. When a Project gets deleted, all Issues will be deleted (using commands). When a Category is deleted, all Issues are assigned to a default category (or none).

Another reason is that the developers who use the backend have to deal only with one concept, not with mixed concepts.

So if Project and Issue are Aggregate Roots, how would I reference them? E.g. a AssignIssueToCategory command would contain the UIDs of both aggregates. Would Issue now simply have a property “CategoryID”?

How would I implement a DeleteCategory command handler? Normally I implement command handlers in the aggregates. Which would be responsible in handling the command? Would Category notify all it’s Issues? In this case it must maintain a list of it’s issues. Would the command trigger some other commands (within the same transaction)? Or should I use a saga here?

Kind regards,
Michael.

Hi Michael,

aggregates will only have to reference each other by their Identifier. If there is a rule that A can only contain one B, a would contain the ID of B to validate that. A Saga can be used to remove the reference from B when A is deleted. If you want to prevent a B from being deleted when an A is referencing it, you’d use a query to validate that.

So in your case, an Event Handler could do a query to find all Issues for a Category and send a command to each one of them to remove the category. In Axon, you could also choose to use the Saga mechanism to implement this. However, it feels too simple and strangeforward to go that way.

Hope this helps.
Cheers,

Allard

Hi Allard,

So in your case, an Event Handler could do a query to find all Issues for a Category and send a command to each one of them to remove the category. In Axon, you could also choose to use the Saga mechanism to implement this. However, it feels too simple and strangeforward to go that way.

But if not that way, which other should I choose? I thought an event handler should not issue commands. Or is this allowed? At least a spacial replay handling would be required.

Hi Michael,

it’s fine to have an Event Handler issue new Commands. That’s basically what a Saga is too. The difference (in Axon) between a Saga and a simple Event Handler is that the latter is always a singleton. Sagas allow you to implement a more complex flow.

About the replays, it important to put this handler in a cluster that doesn’t do replays. Basically, you’d treat it like a SagaManager.

Cheers,

Allard