'How to Approach Domain Modelling with Polymorphic Entity in Domain-Driven Design?
I am rebuilding a software in accounting. This sofware can handle many types of transactions, e.g. sales, inventory restock, and expenses. I have no background in accounting, but I learned some concepts online. I am trying to model the domain with these principles in mind:
- A journal entry consists of several recordings, each of which is either a debit or credit. These recordings' total of debit and credit must be equal (balanced) in a given journal entry
- A journal entry must be created based off of a source document. One cannot simply create a journal entry without any reason.
From those, I conclude that there has to be these entities present:
JournalEntry
entity, which has manyJournalEntryItem
value objects. This entity is responsible for keeping all its entries in a balanced state.SourceDocument
entity, which has some information of transactions being made
Both JournalEntity
and SourceDocument
are each an aggregate root, with JournalEntity
referencing to a SourceDocument
identity.
Now here is the problem. Source documents that are the basis of journal entry creation, can be different things with different behaviors. For example, an 'expense' source document may consists of list of pair of expense category and expense amount, while a 'sales' source document may consists of related inventories, amount of each inventories, and the price of each inventory per one unit.
This makes me think of another approach in the model:
- Create an abstract
SourceDocument
class, with shared properties like date of creation and identity attribute - Create
SalesSourceDocument
,ExpenseSourceDocument
, etc., that are extended from the abstractSourceDocument
JournalEntry
entity will still have to referenceSourceDocument
identity.
While this can make sense, I don't think this is the best way to model the problem in DDD, specifically because it requires to have an abstract entity.
From the sources I read online about DDD, when encountering such problem where I need an abstract entity, I need to separate it into different bounded contexts. However, I'm not sure if it can be done, because a SourceDocument
identity may return a concrete SalesSourceDocument
in 'sales' context, but it may also return nothing in 'expense' context.
What is the best way to approach this problem?
Solution 1:[1]
I think what's important to recognize here is that the source document terminology is to describe the relationship between a journal entry and a document, from a JournalEntry
's perspective. Documents are ARs of their own, which can most likely exist without a journal so why should their abstraction be named after the relationship with another AR?
Source document most likely is a necessary terminology to describe the document relating to a journal entry and chances are it will be part of your model too, but perhaps in the form of a value object such as SourceDocument { id, type }
.
That doesn't mean you couldn't have a Document
abstraction as well if useful: there's no reason to avoid polymorphism at all cost when it's useful.
I may be wrong with that analysis (all models are wrong anyway), but I just wanted to give you a different perspective on that specific modeling aspect.
I am rebuilding a software in accounting. This sofware can handle many types of transactions, e.g. sales, inventory restock, and expenses. I have no background in accounting, but I learned some concepts online.
If you can learn your whole domain online then chances are that it's a generic sub-domain where an off the shelf product can most likely be bought. Rather than going online and try to come up with a model on your own you should be involving domain experts: perhaps your entire vision is wrong here.
Furthermore, given the source documents will most likely live in different contexts, it seems like the Accounting context could be acting as a supporting BC that doesn't have explicit knowledge or dependency on other BCs.
Solution 2:[2]
What is the best way to approach this problem?
Treat your separate use cases as separate until you discover domain concerns that require aligning them.
Your sales documents and your expense documents are different things, and your data model should reflect that: the information associated with each is stored in different tables; the schemas of those tables evolve in responses to changes specific to the domain that they manage.
Duplication is far cheaper than the wrong abstraction -- Sandi Metz
Solution 3:[3]
This is one of those questions where an answer can become quite long so I'm going to do my best to keep it short. I am no expert in accounting but I have had to implement a lot of it in my career. As such I will be developing one of those generic sub-domains when I have time (one day).
One of my recommendations is not to build classification as a code structure. However, it isn't always easy to identify classification. A "simple" example may be a Customer
. We could have a GoldCustomer
and SilverCustomer
. We could subclass these from an abstract Customer
. We have to decide why we are doing this. If, for example, it is to determine that some minimum order amount per period requires and upgrade/downgrade the customer Type
in order to qualify for some discount then that can be easily abstracted into its own concept. If the business decides to add PlatinumCustomer
or BronzeCustomer
then we have to make code changes to inherit from the abstract
class. If we rather model that concept as something that can be defined through additional instances (data) then we do not need to have code structures representing those:
public class CustomerDiscount
{
public int Type { get; }
public decimal MinimumSpend { get; }
public int Period { get; }
public decimal DiscountPercent { get; }
}
What I am getting at is that a generic subdomain that may be purchased off-the-shelf (as stated by @p|a|x) would not know about any concepts in any other domain but would need to be able to represent the requirement as a set of instances that relate to its domain specific language.
At the heart of it accounting is about an Account
that has debits and credits. From there it is a matter of grouping transactions using a Journal
and having some form of PostingRule
that could represent the breakdown of amounts into transactions. If memory serves Martin Fowler has some accounting bits in his analysis patterns book. Your accounts are also classified using a Chart of Accounts (COA) and sometimes these are standardised (SCOA). These classifications are what make things interesting in that they typically also determine your posting rules.
I am no fan of abstract classes in your domain as they tend to complicate matters but, as mentioned by @p|a|x, there is no reason to avoid them totally. I find abstract
classes and inheritance in general are better suited to technical concerns than business domain models. If you have some technical solution to a business bit then you may find them useful. If you do find yourself considering inheritance of any kind try to see if you cannot represent the relationship using some other concept/class.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | VoiceOfUnreason |
Solution 3 |