DDD - price calculation when adding item to order / receipt - domain-driven-design

I'm trying to apply DDD in my latest project. Though I'm having a problem identifying where to put some business logic/calculation.
First I'm going to describe the business flow and then how I'm thinking of implementing this.
Business flow:
It's simply about adding a receipt item(s) to a receipt. However, the price of the item depends on the type of customer and the number of items.
i.e: Customer of type 'A' would like to purchase 2 meals. In his set up, he is allowed to have 1 lunch discounted, but because he is very hungry, he takes 2. The first meal should be for 4 eur (discounted), the second should be 6 eur (full price).
What I'm thinking is:
Product - an entity (of different bounded context, will only be used as an input param) - fields: code, name, ..., collection of prices each for a different customer type.
ReceiptItem - entity -fields: product code, product name, count, price, ...
Receipt as Aggregate root - fields:
customer (entity) - contains collection of ReceiptItems
On the Receipt, there will a method addItem(Product product, int count) and I plan to add the correct price calculation inside this method.
Returning to our example: receipt.addItem('menu1', 2)
So I would check the customer type and how many lunches he is allowed to have discounted. Given the example above, I would see, that he is allowed one. I then need to get the actual discounted price and I would get that directly from the product that exposes a method getCustomersPrice(customerid).
In the end, I will end up with two receipt items being added to Receipt. They will both have the same product code, but different prices. (In case the allowance would be 2 or more, I would only add one item to the collection with the discounted price, but count would the 2).
Would you say, that this is a good approach? I'm worried about passing the product as an input parameter to addItem. As it seems to create a tight coupling.
Thank you.

The Receipt aggregate root should be smaller: Receipt has ReceiptItems, which is fine, but Customer should be its own aggregate root. A customer exists independently of receipts, plus small aggregates are preferred.
Product is also a separate domain object within this bounded context, even though products also exist in a separate bounded context. Product also needs to be outside of the Receipt aggregate.
Given the fact that there are several aggregate roots, the logic to add a product to a receipt touches more than one aggregate. Thus, the logic cannot reside inside the Receipt aggregate nor any other aggregate. Such logic is best placed into a Domain service (which may reside in the same package as the Receipt aggregate), such as:
public class ReceiptService {
void addItem(ReceiptId aReceiptid, ProductId aProductId, int quantity) {
// business logic to determine price per item
// ...
// for each item, forward item creation to receipt:
receipt.addItem(productIdOrName, priceOfItem);
}
}
The Domain service combines data of various aggregates in order to determine the price per item. The internal logic to create a ReceiptItem and insert it into the collection of receipt items is best forwarded to the Receipt aggregate.

Related

DDD UI composition across multiple contexts on creation

Based on this talk from Mauro Servienti called "All Our Aggregates are Wrong. Here is the link for reference: https://www.youtube.com/watch?v=KkzvQSuYd5I
We might have a few bounded contexts each containing information of a item.
e.g
Marketing Context - name, description, images
Sales Context - list price, product code?
Inventory - stock level
Each context might have their own name for it, e.g. inventory might call it sales item.
I think sales might be the owner of an item so when you go to create an item that is the place it will be created.
I have two questions.
Does the UI call these context's seperately?
If I have a UI to create a product/item and takes the data for both marketing and sales (list price, product code, name, description), I believe it is the case that the UI will make two separate calls.
The first call being to the sales context to create the item, which will publish a ItemCreated event or similar.
The second call will be to the marketing context to set the name, description, etc...
Is this correct?
Marketing's item
so my understanding of the inventory service, is that it will listen for the sales ItemCreated event and then create its own aggregate/stream for its representation of an item (Inventory item)
With marketing on the other hand, if I have a UI where I create the item with sales and then need to set the data in marketing, does the UI wait for an item to be created in marketing before then setting the title and description? Does the UI poll the marketing context until the item has been created and then performs the command to update the data?
I did something similar in the past.
The best approach I found is:
Firs you have to find the main owner of the item. In my case it was a product catalog.
Having a product in the catalog means the company intent to work with this product even if there is not stock, nor a marketing campaign, nor anything yet. This is an aggregate by its own, its operation (add product to catalog) is completly transactional because even if the event ProductAddedToCatalog fails to be applied in other Bounded Context (BC) (because infrastructure fails) I do not have to undo nor coordinate/orchestate anything. Just retry raising the event. The Catalog BC is kept in a consistent state.
For the others BC; applying the event means they create its entry with empty/default values. i.e. in Warehouse BC the entry is created with 0 stock and its correlated product catalog ID. In marketing the entry is created as a product with no marketing campaings so far. And so on with others BC.
How I modeled the UI taks for this? I created several screens like a wizard.
The first screen ask for product catalgo info. Its only task is insert a new product on the catalog.
The next screen ask for Warehouse info but it is not inserting; it is updating the entry with default values created by applying ProductAddedToCatalog event that was raised in the first step.
This way, in case warehouse step fails, the system keeps its consistent state and a user could use the UI task for updating product stock in Warehouse BC later.

Confusion with entities and aggregate roots for patients, dentists, treatments and medical history

I am new to DDD and decided to practice it with a dental clinic system I'm developing, but I'm struggling with modeling the domain so an extra pair of eyes will be greatly appreciated.
For this dentistry system, the domain expert told me that a patient holds only one medical history. The medical history must have a Record Number which is unique on the system. The medical history holds dental treatments the patient could have (like planned treatments) as well as treatments that the patient already had. Every treatment has a price, and so the medical history contains a Total price on it (based on planned/applied treatments). Whenever a patient gets a treatment done, he/she will have to pay with at least 50% of that treatment price, meaning he/she will eventually pay the rest of it on future appointments (if no treatment plan exists, he/she will have to pay for the 100% of the price). Finally, this dentistry clinic gives the option to patients to pay on different currencies, because sometimes a patient that comes for the day has only Euros, but then he decides he wants a plan and for future appointments will pay on Pounds.
Based on all this, and my beginner knowledge of DDD, my first thinking is that I have these entities:
Patient
Treatment
Dentist
I will have several value objects, but the most important ones might be:
Money (for prices and currency)
Signature (for applied treatments)
Tooth or Teeth (used on Treatment entity)
And I can only find one aggregate which is Medical history since it puts together patient info, as well as treatments (planned and applied). But this will mean that whenever I update a Medical History, I will have to update patient info and treatments, even if one of those never changed. Patients could change their personal information, which will be reflected in medical history, but it doesn't affect treatments.
I am a bit confused on how to model this. Please help!
Remember that Aggregates, and by extension Bounded Contexts (BC), are a grouping of data and business logic that belong together (and most likely things that need to change transactionally). The data that an aggregate contains is there because the business logic needs it, not because some application screen needs it. This is very important to clear up some confusion and to free you of some constraints in order to design your aggregates.
For example, when you display the Medical History to the user, you might want to show the Patient's name, address, age and so on, and also the treatments prices, but if you think about it, you don't need any of this to manage the Medical History. From what you say, the Medical History has a Record Number, a PatientId, and a list of TreatmentIds with maybe the Dates when they were done.
When you want to display the Medical History to the user, you can use UI Composition. So, you get the Medical History (which is mostly a bunch of Ids and dates). Then from the Medical History's PatientId, you can get the Patients's information from the BC that owns it. From the TreatmentIds, you can get the Treatment descriptions from some BC that owns that and their prices from the BC they belong to.
So, based on that, you can build your aggregates not based on the "relevant names" on your domain like Patient, Treatment or Dentist, but by the business logic they implement.
This is just wild guessing, but I can think of:
BC Marketing (for lack of a better name): Contains the descriptions of all treatments, information about the Dentists, Information about the rooms and materials, etc. So, texts, pictures and other details.
BC Finances: Contains information about the prices of each treatment, payment records of each payment, credits and debits of each patient, etc. In charge of keeping track of all these things. For example, it could know when a treatment starts/ends and depending on the Patient's record, require 50 or 100% payment. There's no need of direct relation to the Medical History here, it only needs to know if it's the first treatment or not.
BC Scheduling: In charge of scheduling new treatments and keep track of when they start and finish. This could contain the History, or it could potentially be somewhere else if necessary.
BC Medical: In charge of keeping all the medical records, allergies, medical details of the status of the teeth, etc.
BC Patients Care: In charge of tracking patients' information, name, nationality, contact details, etc.
Once you have an idea of the Bounded Context you can define the aggregates. There can be one or more per BC. Also, some things might not be an aggregate. For example, the Medical History might not require an actual aggregate if it's basically a record of treatment Ids and the dates they were made and there's no business logic associated (the history is not going to deny a treatment, have opinions on when a treatment should happen and so on, it's just a history).
Don't take this as a recommended design, but just as a thought process to come up with your own solution.
Entities have an Id where as Value Objects have structure identity which means if two value objects have the same value then they are the same.
In case of Money, there is no difference between two $5 bills, so it can be a value object.
You have not described the role and attributes of Tooth and Signature.
In case of Tooth, does it matter whose Tooth is it? Can You replace a patient's tooth with any other tooth which has the same attributes? If it does matter, then Tooth requires an Id therefore it is an entity.
In case of Signature, how are you going to compare two signatures? Do you have an image recognition software that can compare the look of two Signatures and decide that they are the same? You might have two patients with similar looking signatures, should their signature be treated as the same?
If you choose Medical history to be an Aggregate, then you should treat it as one object. Do you want to load the entire Medical history, in order to add a new Treatment to it? Can a Treatment be associated with another Entity, such as Dentist? If you can use a portion of Medical history (such as Treatment) individually then it is not an aggregate.
Some good tutorials:
Entity vs Value Object by Vladimir Khorikov
Entities, Value Objects, Aggregates and Roots by Jimmy Bogard

Tracking tire mileage in MAXIMO as measurements on meter readings

I am helping on a project and we are managing a tire warehouse in MAXIMO. That was OK, but now our business guys want us to track mileage for these tires. As these are stock parts, I do not understand how we can manage these and capture mileage for each tire.
A rotating item is a serialized asset, such as a pump or a tire, that you define with a common item number. You designate an item as rotating because it shares properties of both items and assets. A rotating item can have an inventory value, metered mileage and an issue cost.
A rotating item is an inventory item with a generic item number, a current balance,
and multiple instances that can be used in various locations around a plant with individual asset numbers.
A rotating item cannot be consumed and is maintained as an asset. After creating an item and adding it to a storeroom, you can either use the Assets application to create the asset record for the item you want to track, or create a purchase order for the rotating item and serialize it when you receive it.
When you associate an asset with a rotating item, balances can be displayed and tracked for the item. A rotating item is tracked both by its item number in Inventory records and by its asset number in Assets records. An item cannot be both a spare part and a rotating item.
Avoid using rotating asset/item. It is too complicated to use and very difficult to train on. Many people recommend it as a solution, but in practice, all the customers I've worked with don't like it. Eventually, they learn it but the work flow is completely different from issues and returns. Wait until you have to move the asset from a storeroom to location or vice versa.
You can use item condition code if you want to tires and what percentage tread is left: https://www.ibm.com/support/pages/understanding-condition-codes

How do I interpret this particular feature

lets say I have 3 entities: parent1 <- child -> parent2. I used dfs() and got feature I can't understand MEAN(child.parent2.MEAN(child.num_feature)). Reading documentation I thought about any_entity.MEAN features as "group by entity then apply MEAN" but now this approach doesn't work
Deep Feature Synthesis creates new features by "stacking" existing features. To understand this feature, let’s go through how this is calculated step-by-step.
Calculate feature MEAN(child.num_feature) and add it to parent2.
Join that feature (defined on parent2) into child. This creates a new feature parent2.MEAN(child.num_feature) defined on child. Rows of child that have the same value for `parent2 will have the same value for this feature.
Group the child by parent and take the mean of that feature. This creates MEAN(child.parent2.MEAN(child.num_feature))
To help clarify, let's go through a concrete example
Imagine parent1 was a table of customers, child was a table of transactions by your customers with the column amount, and parent2 was a table of each unique product you sell.
The feature MEAN(transactions.product.SUM(amount)) created for the customers entity could be interpreted as “what is the average total sales of products this customer purchased” e.g “does this customer buy products that have sold a lot”.

Orders & Inventory DDD - Where should allocation/reservation be handled?

I am trying to refactor a legacy order handling and stock system with into a cleaner service oriented event-driven architecture. However, I am having some difficulty deciding what service should be responsible for the reservation/allocation of stock.
A brief overview of the current system
Sales orders are placed with us via third party system but we do not necessarily have all order lines in stock.
If an order item is in stock then we allocate/reserve the stock for that order straight away.
However, if we do not have enough stock then we procure the stock from our suppliers via a purchasing system.
When the item arrives from the supplier, the system will search through all open sales orders for the item and reserve/allocate the available stock to them, prioritising by sales order date. ***
I have already identified two services that I think need to be developed
Sales - Responsible for receiving the sales order and inserting into the database. Has domain entities such as Order, OrderLine etc.
Inventory - Responsible for keeping track of how much stock is available in our warehouse. Has domain entities such as StockItem.
However, as the allocation/reservation of stock concerns both inventory and sales I am not sure where the behaviour in point 2 above should be put.
I welcome any help or thoughts on this.
I think you have 2 BCs (bounded contexts): Inventory and Sales. For the integration between them I would probably go for domain events approach.
When a new item arrives at the warehouse, the Inventory BC increments the stock for the item, and publish an event.
Sales BC subscribes to the event, and it updates the opened sales that are waiting for the stock item.
So, behaviour of "point 2" are shared by both BC:
Sales BC search for opened orders waiting for that item. And then it asks Inventory BC to get the number of items it needs (this request is synchronous) and close the order.
Inventory BC receives the request and decrements the stock for the item.
However, as the allocation/reservation of stock concerns both inventory and sales I am not sure where the behaviour in point 2 above should be put.
I've been thinking about this problem (purely academically), and my current conclusion is that reservation management belongs with the inventory system. That keeps the stock source (the loading of items procured from your suppliers) and the stock sink (fulfillment of orders) together.
So the inventory system caches its own copy of the data required to fill the order (allowing it to work autonomously). It should be able to make progress as soon as it is informed that the suppliers have provided new inventory, even if the sales system happens to be down for maintenance.
You mentioned SOA and NServiceBus, so my initial thought was that you've attended Udi Dahan his ADSD training? I'll assume you have. With that, I'll try to answer your question.
So far I don't have a lot of information. But with what we have, I figured we need these properties to store all that you mentioned.
ProductId, one for each available product
InventoryTotal, attached to a ProductId. This number goes up and down
OrderId, to create an order
OrderDate, to make sure we can find the order that should receive incoming stock first.
If you have an OrderId, you can attach one or more ProductId to create an actual order. Different ways of storing this technically. Maybe in a relational database with Order and OrderLine tables, or possibly in a DocumentDb where everything is stored in a single document. That's totally irrelevant at this point.
Assuming we need 4 attributes, I'm not sure why we would create more than 1 service to split this up? This might change when we have more information, but at this moment I don't see the need.
If you want to discuss this, contact us at support#particular.net, mention my name and we can continue the conversation.
You are talking about loosely coupled domain apps, managing your sales orders, managing your inventory and managing your purchase orders.
Inventory must always be up to date, in order to not sell what you can't deliver. So PO en SO app should both talk to inventory via synchronous (inventory) services. To keep everything consistent, events on purchasing side, like receiving less than you expected for a PO, will have an impact on any SO already assigning quantity of that PO, as persisted in inventory. So the same PO pcs for example, in which the event of receiving less as expected, is registered, should synchronously update inventory, to update the quantity available for SOs to assign from, and publish an event, to be picked up, asynchronously, in the So app, so that the user can be notified and talk relevant action. Etc.

Resources