This may be a basic question but I am pretty new to DDD.
I have an domain object that we'll call Adjustment which can be processed in bulk from the UI. Before we process the Adjustments, we need to validate the date those adjustments will be applied. My problem is with the location of that IsValidDate() method in my domain object.
Should it be a static method in the Adjustment class?
Should it be part of an AdjustmentService class?
Should I create a AdjustmentsGroup domain object to contain a collection of adjustments and which would also implement IsValidDate?
I would tend to think that the 3rd option is the best one but I have a hard time thinking of a domain term for the group of Adjustment objects. Is it ok to "force" a container type domain object for this type of scenario? Is there a common practice to handle this?
Thank you
Edit: IsValidDate actually contains business logic. This is not just a simple date validation method
I would vote for 2) Make it a DomainService. The code to implement it could be in either a DomainServices class, an AdjustmentServices class, or a ValidateAdjustmentService class, depending on what other services are in the domain model, and what makes the most sense from an organizational perspective.
Another option, (if the rules implemented by this service are business rules) is to implement this as a SPECIFICATION. (Check out pages 224 - 240 in DDD)
If you're doing simple is-this-actually-a-date string validation, the correct place for that method, in my opinion, is inside a Service class, as Charles suggested.
However, if data within the Adjustment or AdjustmentCollection objects can change the date validation logic, e.g. disallowing certain date ranges, the method belongs in that object.
I would favor the 3rd option, but create make a more generic function on Adjustment, like Validate(), which would return a collection of errors/validation faults on itself. That way you can add validation rules at a later time without changing your interface. The Validate() function of AdjustmentsGroup would simply call Validate() on each member in the collection. An equally valid (no pun intended) way would also be to have a separate validator class for the object that contains all validation logic.
Your AdjustmentService would then call Validate() on AdjustmentsGroup before processing the adjustments.
Related
I'm trying to follow DDD principles for my current project. Unfortunately I have to use RFCs due to technical constraints, so no OData and no REST. It's quite a long question I hope it's OK to ask this in Stackoverflow.
In any case, I have an entity class WorkOrder with a list of Operation objects.
I have a WorkOrderRepository class with a SAVE method that only receives a WorkOrder object and is able to save everything (header data, address, etc) in one go. No matter if it's creation, update or deleted. The repository hides the BAPI calls from the rest.
Now I want to implement the logic to add/update/remove Operations to the work order object and I'm not sure even if the names I give to the methods are correct. Maybe they should be insert/edit/delete... I'm quite confused with this since in every place I look they use different names.
But the most important are my 2 specific doubts:
Should I have only 1 RFC that receives all the updates to a WorkOrder entity including header, operations? Or should I create 1 RFC per operation that only handles one operation at a time? Bear in mind that the UI mockup expects that the user can add/delete multiple operations before clicking a SAVE button and RFC has implicit commit, and that to my knowledge a DDD entity should be updated always in one call.
Option 1:
FUNCTION ZWORKORDER_HDR_UPD
IMPORTING
VALUE(I_WORKORDER_ID) TYPE AUFNR
VALUE(I_WORKORDER_HDR_CHG) TYPE ZWORKORDER_HDR_CHG
VALUE(I_WORKORDER_HDR_UPD) TYPE ZWORKORDER_HDR_UPD "X structure for the BAPI
VALUE(I_OPERATIONS_CHG) TYPE ZOPERATIONS_CHG
VALUE(I_OPERATIONS_UPD) TYPE ZOPERATIONS_UPD
VALUE(I_OPERATIONS_DEL) TYPE ZOPERATIONS_DEL
EXPORTING
VALUE(E_ERRORS) TYPE BAPIRET2_T.
Option 2
FUNCTION ZWORKORDER_OPERATION_CRT
IMPORTING
VALUE(I_WORKORDER_ID) TYPE AUFNR
VALUE(I_OPERATION) TYPE ZOPERATION_CHG
EXPORTING
VALUE(E_ERRORS) TYPE BAPIRET2_T.
FUNCTION ZWORKORDER_OPERATION_UPD
IMPORTING
VALUE(I_WORKORDER_ID) TYPE AUFNR
VALUE(I_OPERATION_CHG) TYPE ZOPERATION_CHG
VALUE(I_OPERATION_UPD) TYPE ZOPERATION_UPD
EXPORTING
VALUE(E_ERRORS) TYPE BAPIRET2_T.
FUNCTION ZWORKORDER_OPERATION_DEL
IMPORTING
VALUE(I_WORKORDER_ID) TYPE AUFNR
VALUE(I_OPERATION_ID) TYPE ZOPERATION_ID
EXPORTING
VALUE(E_ERRORS) TYPE BAPIRET2_T.
How should my Workorder methods look to handle this? I'm specially confused with the update method, since I'm not sure if I should first get the existing operation and then update it or let the parent class do it. But maybe my approaches are completely wrong from the root.
Option 1:
workorder->add_operation( i_operation ). "Pass flat structure from RFC? Or first create object?
workorder->update_operation( i_operation_chg
i_operation_upd ).
workorder->delete_operation( i_operation_id ).
Option 2:
workorder->add_operation( ).
operation = workorder->get_operation(i_operation_chg->get_id())
operation->update( i_operation_chg
i_operation_upd ).
operation->delete_operation( i_operation_id ).
The simplest solution is always the best (KISS and YAGNI principles). It doesn't really matter if you create 1 or 3 RFC-enabled function module, so if you can achieve your goal with one function module, then do it with one.
I think you need to have two RFC-enabled function modules. One to validate the maintained operations (do the validations as far as possible), but that should not save anything to the database, and another one called after the user clicks the SAVE button, to save the whole "WorkOrder", including the maintained operations (at this time, there will be the complete validation also).
If you don't need to define an "operation" class for something else, right now, then keep it simple, no need to instantiate an object. Note that you may create an "operation" class with private static methods, and being a friend of the "workorder" class (only this class can use the operation class), just to organize better your code.
PS: although I don't know what is "Domain-Driven Design", I don't see how your question is related to it, because it just looks like simple program design.
We also work with DDD, although luckily not with RFCs but instead Gateway/OData. Since REST by defintion is stateless we always commit in our application service layer.
What we do is have three "DDD" entities
app_Service, domain_service, repository (and also some data containers like aggregates), where the app_service in your case would expose the methods create, update and delete, and possibly also validate.
Then I would write four thin RFCs (crud + validate) that basically passes the data to the app service.
As for being able to handle multiple updates in each save, we always model our OData (your RFC) after the UI requirements, then it's the app_service task to make sense of the data from a system point of view.
We make heavy use of Abap Clean Code (it's even part of our ATC check), and they clearly state that you should have seperate methods, which is also OO best practices.
https://github.com/SAP/styleguides/blob/master/clean-abap/CleanABAP.md#split-methods-instead-of-adding-optional-parameters
I've just run into a problem while trying to re-design our existing business objects to a adopt a more domain-driven design approach.
Currently, I have a Product Return aggregate root, which handles data related to a return for a specific product. As part of this aggregate, a date needs to be provided to say what month (and year) the return is currently for.
Each product return MUST be sequential, and so each product return must be the next month after the previous. Attempting to create a product return that doesn't follow this pattern should result in an exception.
I had thought about passing along a Domain Service to the method (or constructor) that sets the PeriodDate for the return, but I'm at a loss for how I would do this. Even if the domain service had a reference to a repository, I can't see it being appropriate to put a "GetNextReturnDate()" on that repository.
For background, each product return is associated with a product. I was reluctant to make the product the aggregate root, as loading up all the product returns just to add one seemed like an extremely non-performant way of doing things (considering this library is going to be used with a RESTful Web API).
Can anyone provide suggestions as to how I should model this? Is it a matter of just changing the aggregate root and dealing with the performance? Is there some place in the Domain that 'query' type services can be placed?
As an example, the current constructor the for product return looks like this:
public ProductReturn(int productID, int estimateTypeID, IProductService productService)
{
// This doesn't feel right, and I'm not sure how to implement it...
_periodDate = productService.GetNextReturnDate(productID);
// Other initialization code here...
}
The IProductService (and it's implementation) sit in the Domain layer, so there's no ability to call SQL directly from there (and I feel that's not what I should be doing here anyway)
Again, in all likelihood I've modelled this terribly, or I've missed something when designing the aggregate, so any help would be appreciated!
I Think my broader problem here is understanding how to implement constraints (be it foreign, unique, etc.) inside of a domain entity, without fetching an entire list of returns via a domain service, when a simple SQL query would give me the information required
EDIT: I did see this answer to another question: https://stackoverflow.com/a/48202644/9303178, which suggests having 'Domain Query' interfaces in the domain, which sound like they could return the sort of data I'm looking for.
However, I'm still worried I'm missing something here with my design, so again, I'm all open to suggestions.
EDIT 2: In response to VoiceOfUnreason's answer below, I figured I'd clarify a few things RE the PeriodDate property.
The rules on it are as follows:
CANNOT be NULL
It MUST be in sequence with other product returns, and it cannot be in a valid state without this fulfilled
It's a tricky one. I can't rely on the date passed in because it could then very well be out of sequence, but I can't figure out the date without the service being injected. I am going to transform the constructor into a method on a Factory to remove the 'constructor doing work' anti pattern.
I may be overly defensive over the way the code is currently, but it feels the amount of times the ReturnService has to be injected is wrong. Mind you, there are lots of scenarios where the return value must be recalculated, but it feels as though it would be easy to do this just before a save (but I couldn't think of a clean way to do this).
Overall I just feel this class has a bit of a smell to it (with the injected services and whatnot), but I may be needlessly worrying.
I had thought about passing along a Domain Service to the method (or constructor) that sets the PeriodDate for the return, but I'm at a loss for how I would do this.
I strongly suspect that passing a domain service to the method is the right approach to take.
A way of thinking about this: fundamentally, the aggregate root is a bag of cached data, and methods for changing the contents of the bag of data. During any given function call, it's entire knowledge of the world is that bag, plus the arguments that have been passed to the method.
So if you want to tell the aggregate something that it doesn't already know -- data that isn't currently in the bag -- then you have to pass that data as an argument.
That in turn comes in two forms; if you can know without looking in the aggregate bag what data to pass in, you just pass it as an argument. If you need some of the information hidden in the aggregate bag, then you pass a domain service, and let the aggregate (which has access to the contents of the bag), pass in the necessary data.
public ProductReturn(int productID, int estimateTypeID, IProductService productService)
{
// This doesn't feel right, and I'm not sure how to implement it...
_periodDate = productService.GetNextReturnDate(productID);
// Other initialization code here...
}
This spelling is a little bit odd; constructor does work is usually an anti-pattern, and it's a bit weird to pass in a service to compute a value, when you could just compute the value and pass it in.
If you feel like it's part of the business logic to decide how to compute _periodDate, which is to say if you think the rules for choosing the periodDate belong to the ProductReturn, then you would normally use a method on the object to encapsulate those rules. On the other hand, if periodDate is really decided outside of this aggregate (like productID is, in your example), then just pass in the right answer.
One idea that may be crossing you up: time is not something that exists in the aggregate bag. Time is an input; if the business rules need to know the current time to do some work, then you will pass that time to the aggregate as an argument (again, either as data, or as a domain service).
The user can’t pass a date in, because at any given time, the date for a return can ONLY be the next date from the last return.
Typically, you have a layer sitting between the user and the domain model -- the application; it's the application that decides what arguments to pass to the domain model. For instance, it would normally be the application that is passing the "current time" to the domain model.
On the other hand, if the "date of the last return" is something owned by the domain model, then it probably makes more sense to pass a domain service along.
I should also mention - a return is invalid without a date, so I can’t construct the entity, then hope that the method is called at a later time
Are you sure? Effectively, you are introducing an ordering constraint on the domain model - none of these messages is permitted unless that one has been received first, which means you've got a race condition. See Udi Dahan's Race Conditions Don't Exist
More generally, an entity is valid or in valid based on whether or not it is going to be able to satisfy the post conditions of its methods, you can loosen the constraints during construction if the post conditions are broader.
Scott Wlaschin's Domain Modeling Made Functional describes this in detail; in summary, _periodDate might be an Or Type, and interacting with it has explicit choices: do this thing if valid, do this other thing if invalid.
The idea that constructing a ProductReturn requires a valid _periodDate isn't wrong; but there are tradeoffs to consider that will vary depending on the context you are operating in.
Finally, if any date is saved to the database that ISN’T the next sequential date, the calculation of subsequent returns will fail as we require a sequence to do the calculation properly.
If you have strong constraints between data stored here and data stored somewhere else, then this could indicate a modeling problem. Make sure you understand the implications of Set Validation before you get too deeply invested into a design.
Your problem is querying across multiple aggregates(product returns) for decision making(creating a new product return aggregate).
Decision making based on querying across aggregates using a repository will always be wrong; we will never be able to guarantee consistency, as the state read from the repository will always be a little old.(Aggregates are transaction boundaries. The state read from repository will only be correct at that very instant. In the next instant the aggregate's state may change.)
In your domain what I will do is create a ProductReturnManager AggregateRoot which manages returns for a particular product and a ProductReturn Aggregate which specify one specific return of the product. ProductReturnManager AggregateRoot manages the lifecycle of ProductReturnAggregate to ensure consistency.
The logic for assigning a next month sequential date to ProductReturn is in ProductReturnManager (basically ProductReturnManager act as a constructor). The behavior of product return will be in ProductReturnAggregate.
The ProductReturnManager can be modeled as a Saga, which is created on first CreateProductReturnCommand (for a productId), and same saga is loaded for further CreateProductReturn commands(correlated by productId). It handles ProductReturnCreatedEvent to update its state. Saga creation logic will be according to your business rules(Eg. saga creation is done on InvoiceRaisedForProduct event and handles CreateProductReturn commands.)
sample code:
ProductReturnManagerSagaState{
ProductId productId;
//can cache details about last product return
ProductReturnDetails lastProductReturnDetails;
}
ProductReturnManagerSaga : Saga<ProductReturnManagerSagaState>,IAmStartedByMessages<CreateProductReturn>{
Handle(CreateProductReturn message){
//calculate next product return date
Date productReturnDate = getNextReturnDate(Data.lastProductReturnDetails.productReturnDate);
//create product return
ProductReturnAggregateService.createProductReturn(Data.productId, productReturnDate);
}
Handle(ProductReturnCreatedEvent message){
//logic for updating last product return details in saga state
}
}
ProductReturnAggregate{
ProductId productId;
Date productReturnDate;
ProductPayment productPayment;
ProductReturnState productReturnState;
//commands for product return
markProductReturnAsProcessing();
}
This is an excellent video by Udi Dahan on working across multiple aggregates.
So I have few doubts regarding calling something as domain object (and eventually placing the class under domain package) or not.
I have a micro-service whose responsibility is to do some calculations (without getting into actual business requirements, all it does is calculate some return of intereset based on given request). Now to achieve the calculations there are certain sub-calculations which need to take place and hence are composed in different classes respectively. But yes, these calculation do not need to be persisted in DB , and neither they have an ID (so definitely not an Entity or Aggregate). However these individual calculator classes (for the lack of terminology) do contain some complex business logic. Now, my question is, do these individual classes still qualify/classify as domain objects or should they be referred to as services ?
Feel free to ask for more clarifications around use case if need be.
Cheers !
Now, my question is, do these individual classes still qualify/classify as domain objects or should they be referred to as services
From the DDD point of view, in the Domain layer, there are the following terms that could be implemented using classes: Domain entities, Aggregate roots (a type of Domain entity), Value objects and Domain services.
Because your things don't have an Identity they cannot be Domain entities or Aggregate roots. Calculations could be done inside Value objects or Domain services. Value objects contain specific behavior related to values so most probable your calculations are implemented using Domain services.
From my experience, not every domain class must be a DDD building block, they could be just classes extracted for better maintainability, Single responsibility principle (SOLID in general) etc.
A simple test could be asking below questions -
Does your “calculator” (as you refer it) hold the calculation result as an immutable state? — if the answer is yes then it is a Value Object.
Is the “Calculator” stateless and only exposes a “calculate” behaviour which accepts a request & returns a calculation result? — if the answer is yes then it is a Service, however, the “result” returned by this service may be classified as Value Object.
I would say that your calculations can fit well either in Value Objects or Domain Services.
How to differentiate? Well, I understand Domain Services as services (well, obvious) with business logic (such as your calculations) that require some kind of external dependency you need to inject in order to get your logic work.
On the other hand, if you can name that business logic as a business concept (i.e. CustomerFee, CustomerCommission, etc) and you don't need any injected dependency to make it work I would say it's a Value Object.
For instance, imagine that you want to calculate the price of a reservation which depends on the fee you will charge to the customer (among other params):
ReservationPrice(CustomerFee customerFee, ItemPrice ItemPrice)
Now your CustomerFee is also calculated based on (say any variable) something.
This way you are modeling your calculations just with Value Objects which allows you to show in your code all the different business concepts they depend on. Also anyone looking at your code and files structure can get an idea about what you are calculating.
Take this example. A Supervisor domain class exposes a method
GetUnderlings(DateTime from, DateTime to) which will return all those people supervised by the given supervisor in the given period.
The method should go here for pleasing semantic reasons. But should go somewhere else for DDD purity. That's because I'm assuming to implement the method one would need to use a Repository, which it seems wrong to embed inside the Domain Entity. In that case, the method should go on a Respository or Service - GetUnderlings(Supervisor supervisor, DateTime from, DateTime to)
How do others handle this scenario?
EDIT: I think the forces can be described like this: according to OO principals, I want the public interfaces of my entities to expose a rich set of business-oriented functionality. BUT according to DDD implementation principals, the implementations of such methods might best be located elsewhere. In a service, for example.
How can this apparent conflict be resolved? The ways I can see are:
have the entity have a reference to the service, or service interface
Always make the client go to the service, not to the entity directly (result: loss of coherence, and totally not cool from an OO perspective)
use "Domain Events" (?)
use some AOP trick to delegate implementation of a method to the service.
If Supervisor is an Aggregate Root it is valid to return Underlings list from Supervisor but just READONLY collection because Underlings shold be modified by Supervisor to apply domain rules and invariants to the modify action. (basic rule not only in DDD, is just well OOP design)
Underlings seems like a history entity. In most of the cases (I do not have enough context info to afirm this in your case) history entities are not aggregate roots and ONLY aggregate roots have repositories.
Keep in mind that if the retrieve of Undelings is for UI (not to apply an action with rules and invariants) you do not need to care about aggregate roots, entities, etc, because you should apply CQRS and use view services to retrieve plain data (1st normal form, not aggregate roots) to show it to the user. When an action is trigger by the user UI you need to check rules (that means apply DDD); you retrieve Supervisor from Repository, check Underlings ( remember, readonly collection) to take decissions, apply the action and save changes.
Supervisor should have a collection of Underlying if they belong to the Supervisor aggregate.
like
class Supervisor {
private Collection<Underlying> underlyings;
}
Then the GetUnderlings(DateTime from, DateTime to) is filtering the undlyings. This is fine.
But if there are too many underlyings belonging to a Supervisor, this solution is not friendly to performance. In this case, I'd like to use make Underlying an aggregate root and use it's Repository to retrieve the result like:
interface UnderlyingRepository {
Collection<Underlying> GetUnderlings(Guid supervisorId, DateTime from, DateTime to);
}
The client(maybe a MVC controller) invokes the repository directly. Then the problem is how to protect the invariants of addUnderlying which used to be protected by the Supervisor aggregate.
You could use either DomainService or DomainEvents.
The solution above is based on traditional DDD architectural model. Like #jlvaquero said, you could use CQRS instead.
A common approach is to expose all Supervisor's Underlings as read-only collection. And if you need to implement a method that filters them by date range you simply add this method to class Supervisor as GetUnderlings(DateTime from, DateTime to) and everything works.
If common approach does not work since your Supervisor has lots of Underlings, or it is time-consuming to retrieve all these Underlings, or ... there is a workaround - 'Separated Interface' (PoEAA) pattern by Martin Fowler.
You can define an interface of component that returns Underlings within specific date range in your Domain Model, but implement it in another layer (e.g. Data Access layer).
In this case your domain entity has no reference to service and it does not exposes any 'Underlings'. All clients that need to get 'Underlings' call service and pass an instance of 'Supervisor' into method and date range.
In domain driven design, it appears to be a good practice to use Factories to create your domain objects in your domain layer (as opposed to using a direct constructor or IoC).
But what about using the domain object factories in a presenter layer. For instance, say that I was creating a domain object from user input obtained from the presenter.
Here's an example, say I have a Configuration domain object that has a number of decimal settings.
public class Configuration : PersistantObject
{
public decimal temperature {get;set;}
...(times 20)
public decimal gravity {get;set;}
}
In order to create this object in the domain layer, rather than the presenter layer, I would have to pass each of these decimal values as function parameters. Creating an unwieldy function definition and call.
ie ConfigurationService.CreateConfiguration(temperature, ...(x20), gravity);
The perhaps better solution would be to create the Configuration object in the presenter layer, and assign all the values of the configuration object directly from the user input, skipping a lengthy function call.
Configuration config = ConfigurationFactory.CreateNewConfiguration();
config.temperature = temperature;
..(x20).. = ...;
config.gravity = gravity;
ConfigurationService.SaveNewConfiguration(config);
But I'm wondering if this approach is wrong and why?
If both of these approaches are wrong, what is the best approach for creating a lengthy object from user input and why?
Thanks!
I'd advise against letting your domain objects out of the domain layer and into the presentation layer. Keep the presentation layer focused on presentation.
For this reason, I construct Data Transfer Objects to shuffle data to and from the domain and presentation layers. In your case, have the dialog populate a DTO that is passed to your service and translated into the corresponding domain object.
You wouldn't want to construct domain objects from DTOs every time, though. Consider the case where a DTO represents only a subset of a domain object. Re-constructing an existing domain object from such a DTO would give you a partial domain object. You'd probably want to maintain a light-weight cache that held the full domain object so you could do a proper update.
Essentially, you'd arrive at the DTO solution if you applied the Introduce Parameter Object refactoring.
There are two main ways I would handle this
1) If this is setup through a dialog I would create classes implementing the command pattern and bind a dialog with the object in question. For example CmdCreateConfigurationService, and CmdEditConfigurationService.
CmdCreateConfigurationService would rely on a factory class and minimum parameters you need to select the correct Configuration Service.
You setup a IConfigurationServiceEditor interface and pass that as one of the parameter to CmdEditConfiguration Parameters. With the IConfigurationServiceEditor interface you define as many methods as you need to make the transfer of information from and to the dialog easy and painless as possible. I recommend using a collection of keys and values.The Command Object knows how to setup up the Configuration Service from this collection. The Dialog know to expect this collection when setting up.
Regardless of the data structure you will do the work of filling out the Configuration Service in the command object. By having non dialog/form/screen object implement IConfigurationServiceEditor you can automate your testing and in certain circumstances make configuration of complex objects easiers.
I developed this method for a CAD/CAM softaware that has several dozen parametric shapes each having from 4 to 40 entries.