0

CLARIFICATION: There's a few articles around this point, I am trying to understand based on my use-case, what would you recommend as the AGGREGATE ROOT

The problem

Given a domain that allows creating new invoices with a constraint that each invoice must be unique (Identified by a code, set by the issuer's system, eg: INV-1021) per customer.

The issuer's system and how they generate those codes are completely outside of our scope, all we care about is that to make sure that when our system/domain persists an invoice it's unique in our system per customer, to avoid potential problems.

The solutions I currently have are:

  1. Invoice as an aggregate root: Create a repository implementation (Domain Service) that find an invoice by code and customerId and I could use this interface in my Domain as a business invariant check to the creation/registration behavior for a new Invoice Aggregate Root but since this might not be the responsibility of the Invoice AR, I could just move the behavior of creation to the domain service, without leaking logic outside of the Domain Layer.
  2. Company as an Aggregate Root: While searching for an answer I found this article and it made me think, it might make more sense to have Company as the AR and by loading the AR, it would be a simple memory check in the domain (I don't think it would be a problem from a performance standing point), but the problem is that the only Company behavior is invoice creation, but the rest of the domain logic is all executed on the invoice itself, so even though company is my scope (referencing the article) but it does not feel right and also I am not sure if it's worth the violations of having to manipulate the invoice via the Company AR all the time (99% of the logic of this domain) and not saving the Aggregate Root as whole because I will be interested in modifying the Invoice and not the Company.

Notes:

  • This function is not highly concurrent, however it's likely that a user make a mistake of uploading/registering the same invoice twice.
  • I have read some comments that says that duplication is a database concern but I am not sure if that would be applicable here, since the uniqueness is a business constraint that's been mentioned several times by domain experts.
  • Listing my options helped me feel that the first solution seems like the way to go, but being a DDD newbie, I am interested to hear the opinion of a more experienced person.
2
  • "uniqueness is a business constraint that's been mentioned several times by domain experts" - well, that just means that you should express it somehow within your domain model, it doesn't necessarily mean that you should implement every conceivable aspect of it. In other words, you can express the high-level behavior/policy in the domain model, but delegate details to the storage - there's a balance to be made there, of course, but after all, a db is nothing but a remote service you can ask questions of. Jul 5, 2021 at 19:15
  • Thanks @FilipMilovanović, I completely agree, the technical implementation will live outside of the domain and domain will just define the interface to capture this requirement. My question is more about should this be in the InvoiceAR, Invoice Domain Service or treating the company AR and the invoice is a normal Aggregate and listed my worries with the second approach. Let me know if I can make my question more clear or if I missed your point. Jul 5, 2021 at 19:19

2 Answers 2

3

This is a fairly common problem and im sure if you search here you'll find other questions around invoice numbers and the fact that the legal requirements around them don't play nice with software design.

The problem is that incremental invoice numbers are specifically designed to force the company accounts to be the "Aggregate Root" or in accounting terms, the yearly accounts have to have ALL the invoices, no extra secret ones, no deletions, no changed ones!

But when your eCommerce system generates an invoice, you obviously cant load in a whole financial years worth of invoices! It doesnt make technical sense to have the company as the AR.

So you just have to break the rules, as Robert notes, a database is quite capable of generating an acid compliant incremental number on write and you can hack together methods to make it work. The only problem is knowing what to call the resultant code in DDD. maybe Domain Service? yeah that sounds like it would work on a power point.

5
  • Thanks for the answer @Ewan, yes I found various answers but since this is a very important part and I am a DDD newbie, I thought I'd get an opinion on what the Aggregate Root should be, to give you more context, the company is basically a user that uploads invoice to our system and we turn this invoice into a tradable security, that's the main part, we don't really care about the ID structure or it's sequence, the validation is mainly to avoid our team working on the same invoice twice. So in the light of that, does InvoiceAR makes more sense to avoid manipulating invoices behind companyAR? Jul 5, 2021 at 19:26
  • hmm that does change things a bit, since the invoice has already been generated by the company and they are uploading it to you, surely you don't need to worry about the invoice number constraints. Its just whatever the company tells you it is?
    – Ewan
    Jul 5, 2021 at 19:34
  • Exactly, the only thing our business experts care about, is not to have the same ID (whatever the structure is) twice for the same issuing company, because that creates back and forth, bad customer experience, cost overhead on our company, etc. Another reason is we have a new requirement that's being confirmed, is to have it unique only within 1 fiscal year, so the code gives better control to mitigate such change in requirements in my opinion. Jul 5, 2021 at 19:37
  • well i guess the reasoning is different but the answer is the same, make it a unique field in the db and then work out what its called in DDD. I think this is even less of a "problem" from a DDD standpoint as your repository or data layer is fine as a domain object to which you save ARs and can throw an error or do a check no problem. Its the on creation aspect of standard invoice numbers which is problematic
    – Ewan
    Jul 5, 2021 at 19:47
  • Cool, thanks @Ewan for your input, much appreciated. Jul 5, 2021 at 21:34
2

Let's think this through from the ground up, ignoring all "theory" for the moment. Does the database (a part of your application) have sufficient functionality to do this for you? If yes, wouldn't implementing it yourself be essentially re-inventing something that already exists? Wouldn't that be wasted effort then? Isn't the database in a better position to decide whether an id is unique or not anyway?

Conversely, if there is some "pattern" or "architecture" that forces/pressures you to re-invent stuff that is already there, is that something that you still want to follow?

I am not saying DDD is this way or that way. There's multiple interpretations, yours is probably as good as anyone else's. I'm just saying, maybe we should take a step back and look at what a solution should look like and keep that in mind.

4
  • Hi @Robert Bräutigam, thanks for the answer and the reminder not follow books like a religion. Yes the database can definitely do it, however I actually do like being able to encapsulate the domain within a single layer, this has two benefits in my humble point of view, first it allows me to easily test this invariant in a unit test without spinning up a database and secondly changing code is much easier than changing the database, it makes spinning up different environments easier, migrating database easier and changing the specifics of the constraint easier. Jul 5, 2021 at 19:31
  • BTW "it makes spinning up different environments easier, migrating database easier and changing the specifics of the constraint easier" are all incidents that happened on the same product, that I am trying to refactor into a pattern or using a process to help us mitigate these things easier Jul 5, 2021 at 19:32
  • 1
    @MazenElkashef You know your requirements/preferences, ultimately it is up to you to evaluate all trade-offs. Obviously. I'm just talking about my experiences. We did this in the early days of JEE all the time. I mean re-implementing stuff from the database, because we wanted to be database agnostic. It was a mess and a lot of wasted work every single time. Your mileage may vary however. Jul 5, 2021 at 19:39
  • Thanks @Robert Bräutigam, as I said I am new to DDD and I just wanted to see if I completely missed an aspect in DDD or in SW engineering but it seems like I am not too far from the "right" implementation, I just wanted to make sure I am not completely off, or if there is a design smell, etc. cheers! Jul 5, 2021 at 19:42

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.