I am working on a large configuration driven data management system. The system is built by specifying pieces of configuration (like business rules, processes and validations) in an external syntax. Let's just say that syntax was a conglomerate of a Groovy based DSL and Drools.
I like the ideas of simplicity that DDD offers, specifically separating out infrastructure concerns from the core concepts of the domain.
However, I am struggling to apply some of the concepts of DDD due to the configurable nature of the system. Both behavior (processes), validation, and business rules are defined external to the system. So the entities in the domain intrinsically have not behavior of their own. Rather they delicate to the "Validator" thing or the "Rules Engine" or the "Workflow Engine".
I will clarify with an example. Let's say that my system manages Employees for a Company. Without too much thought, you would imagine that I have an Employee Entity and a Company Entity in my domain.
Following DDD, I am trying to model a scenario where an employee is promoted. You might see a new method come about on the Employee called promote (Employee.promote). We could have a business rule, indicating that an employee cannot be promoted twice within the same year (yes this is all made up). I could therefore have something like:
public void promote( EmployeeLevel newLevel ) {
if ( hasBeenPromotedThisYear( this ) {
throw new InvalidPromotionException
Well, in the application I am working with this business rule would be externalized to a Rules engine. Following DDD, I could do something like:
if( promotionRules.isEligibleForPromotion(this)
To externalize my rules. However, the system is much more generic than this. The "promotion" operation itself is defined as a "Process" through external configuration. So, at compile time, I wouldn't even know if I have a "promote" operation available for this employee. So my employee object becomes pretty bare from a code perspective, delegating all functionality to the configuration. It might look something like:
public class Employee {
public void execute( Process process )
Or alternatively
public class EmployeeProcess {
public void process( Employee employee )
My question is: does DDD even make sense in this application? Should I instead just model the collaboration of processes, validations, business rules (Rules engine) in a non DDD sense?
I like the Onion Architecture, and can use UI -> App Services -> Core -> Infrastructure to keep a nice separation of concerns. But the Core could be the collaborators mentioned above, as opposed to real "domain concepts".
Part of me believes that, in this case, the "domain concepts" ARE the validators, processors, business rules because they make up the ubiquitous language that we talk about when we discuss our system. In this case, I would have Entities with no real behavior (for the most part), and domain concepts in terms of Processors, Validators, Rules Engine that realize the behavior in the system.
Adding a little more info. Given my question above, I was working toward a solution that would look like:
org.example.app
org.example.domain
org.example.domain.shared
org.example.infrastructure
Hopefully, this little snippet adds a little clarity.
So, the Process, BusinessRule, and Validator concepts would lie inside the domain, but would be supporting the domain model in terms of the things the system does.
From Wikipedia:
Domain-driven design (DDD) is an approach to developing software for complex needs by deeply connecting the implementation to an evolving model of the core business concepts.
I believe that validator, process, rule are not your core business concepts. Those are rather common software abstractions.
I'm not big fan of "by-the-book" DDD, but in order to be more "domain-driven" your DSL and your rules actually should be built around your business concepts to give more sense to that.
Under the hood, you can still use validators, processes, managers, executors etc., but your DSL/rules will be more readable if you use business concepts in them, rather than software abstractions.
UPDATED: Since you are using Groovy for defining your DSL, you can use Groovy's dynamic method name resolving and builder functionality to create readable rules and classes. Also you can leverage "convention over configuration" principle to hook in some logic. For example, in Groovy you can try to construct something similar to:
if (employee is "promotable") {
start "promotion" for employee
}
is
will be a method on a base domain object that will check for existence of let's say EmployeePromotableValidator class, which by itself can also be a Groovy class that leverage Groovy's DSL expressiveness.
class EmployeePromotableValidator extends Validator<Employee> {
boolean validate(Employee employee) {
employee.age > 25 && employee.workingYears > 2
}
}
start
will be a method on your base rule script that will search for EmployeePromotionProcess
class, that again can be a Groovy class.
Specification pattern in this case is very simple, because it basically becomes part of the language:
if (employee is "promotable" &&
employee is "advanced" &&
employee.salary < 10000) {
start( "salary increase", "10%" ) for employee
}
In general, DSLs with the help of (semi-)functional languages like Groovy/Scala can be used to hide software abstractions and make your business logic more prominent in the code. With plain Java you will end up with a lot of boiler plate code that will eventually hide all your intentions.