A place for everything,
everything in its place
●
I create software – holistic approach
● Technology (Java, Java EE) and Craftsmanship ● Design, architecture, usability
● Exploration of methodologies and best practices
●
I work as a trainer and consultant
● Java technologies
● Software engineering ● Community
● Leader of Lublin Java Users Group ● Blogger, speaker, publisher...
●
Challenges of software
●Domain Driven Design
● introduction
● advanced topics
●
Technical and architectural
aspects of implementation
●When to use DDD
●
Beyond DDD
Business software
●
No sophisticated stuff
● nothing like Fast Fourier Transform
●
Just simple operations...
● just multiplied by 1000:)
● synergy of simple components ● continuously changing
● We need a structure for dozens of
●You read code and understand nothing
● You can't see any rules or process
●You don't know how to translate
business requirements to code
●You change code and nothing works
●Now it's Your problem:)
Complexity
●
Essential
● inherent and unavoidable ● depends on problem
●
Accidental
● caused by chosen approach ● depends on solution
The Second Law of Thermodynamics
Entropy does not diminish
Cowards solution
Cowards solution;)
What would You change...
if You start everything from the beginning?
XML
Annotations
New server
New version
of the server
SOA
Dynamic language
Another web
framework;)
Insanity:
doing the
same
thing
over and over
again
Where is the problem?
Data access layer
Logic layer
It's all about the model
●
Model needs care
●
Model is a heart of the system
● reason why creating it ● main value
● advantage factor
●
Modelling is the toughest part
● weak model leads to failure
Unstructured design
Just adding new features – with no care about
model
● big ball of mud
● chaos and anarchy ● costs in long term
What model IS NOT
Database is
not
a model
●
just data structure – very technical
●no behavior
●
static – we can't see whats going on
What model IS FOR
●
continous knowledge gathering
●about rules
●
about dynamics
●
common
(client … developer) understanding of
domain
●
vocabulary
●jargon
What model IS
Real model
●
description and simplification for current needs
●no unimportant stuff – avoid mental overload
●knowledge base - rules and behavior
●General concept
● technology and platform independent
●Set of design and analysis techniques
● focus on domain logic layer ● complexity reduction
● extension-ability and changeability
●Pragmatic approach to OOD and OOA
„There is technically nothing new
or revolutionary in DDD,
there is only a guide to a better way of thinking.”
●Focus on behavioral model
●Common (Ubiquitous) Language ●Strategic modeling
● Core Domain ● Contexts
●Domain Layer – OOD ● Data and rules
● Roles that implicate responsibility ● Responsibility Driven Design
● GRASP ● SOLID
Language of the model
If domain expert don't understand
model
Problems with models
●
No model
●
just add features (somewhere;)
●
Model at the beginning, but no maintenance
● useless, inadequate documents
●
Distinct jargon leads to need of translation
● translation leads to errors and misunderstandings
●
Pure analytical model – fails fast
● unimplementable models are useless
Solution: Ubiquitous language
●
Model must be bind to code
● modeling in right paradigm – there is no pure
modeling support for procedural paradigm
● OOD techniques used by annalists ● building blocks...
●
Modeler must code
● coding is not low-skilled job
● result: creating implementable models
“Good picture is worth more than 1000 words”
●Not only UML
●Use any form that supports common understanding
and communication
● Client specific diagrams ● Sketch
System metaphor
●
Software design tend to be abstract
● hard to grasp
●
Organize design around metaphor
● absorb vocabulary
●
Find metaphor in real world that
describes your system
● loose, easy do understand ● large scale structure
Domain Model
Analysis Domain ModelDesign
+ ==
Domain Logic LayerHow to bind model and code?
●
Responsibility Driven Design
● Define roles that objects can play ● Roles determines responsibility
Layers
User interface
Infrastructure Application logic
Domain logic
Thin layer - just coordinates and delegates to domain
Heart of the system – model of the business concepts and rules
Technical capabilities - may exist as a few layers (persistence, messages, etc)
Presentation – can be very sophisticated; do not underestimate
Building Blocks of Domain Layer
●
Entities (not anemic)
●
Value Objects
●
Aggregates
●
Services (business)
●
Policies and Specifications
●
Business events
●
Factories
Entities
●
Object that need to be distinguished
● even if attributes are the same ● some ID
●
Not only data
●
Also behavior (business responsibility)
● characteristic ● essential
Value Objects
●
Just description of some characteristic
●No distinction – no identity
●
VOs are the same if their attributes are the same
●Typical usage:
● attributes of call between objects
●
Usually immutable - because has no identity
● therefore can be reusable – we don't care which instance
is being used
●
Usually no need to persist
Power of VOs
●
Expression of business concepts
●adds
conceptual power
●
more meaningful than just String
●
May contain useful methods (instead of utils)
●validation
●
constructor may validate input
●Examples
● color, point (source, destination) ● address*, phone number
Candidates for VO
●
Strings with format limitations
● zip code
● name
●
Numbers with limitations
● percentage ● quantity
●
Business object's arguments/returns
● money – composed: amount + currency
– maybe can be exchanged?
● address
Services
●
Business Services
● Sometimes it just isn't a thing
● OO is not always proper approach
● OO is not always possible (human factor)
●
When can not find natural "home" in Entity/VO
●Operation without state
● Defined in terms of other Building Block
●
Should not strip Entity/VO of all their
responsibilities
Aggregate
●
Cluster of objects (Entity/VOs)
● inner objects can reference each other inside ● boundary with one root
●
Outer entity is Aggregate root
● controls access - encapsulation
Encapsulation is virtue
human.getDigestionSystem().
getPeritoneum().getStomach(). add(new Sausage(2));
human.getDigestionSystem().
getPeritoneum().getStomach(). add(new Sausage(2));
human.eat(new Sausage(2));
public void eat(Food f){ if (! iLike(f))
thow new IDontLikeItException(f); this.digestionSystem.swallow(f);
}
human.eat(new Sausage(2));
public void eat(Food f){ if (! iLike(f))
thow new IDontLikeItException(f); this.digestionSystem.swallow(f);
@Entity
public class Order{
@Id private OrderId id;
@OneToMany private List<OrderItem> items = new ArrayList<OrderItem>();
private BigDecimal sum = new BigDecimal(0);
//.... status, createDate, rebatePolicy, productRepository,...
public void add(ProductId id, int quantity){
Product p = productRepository.load(id);
OrderItem oi = orderItemFactory.build(p, quantity, rebatePolicy); items.add(oi);
sum = sum.add(oi.getCost()); }
public void submit(){
if (status != Status.NEW)
throw new InvalidStateException();
status = Status.IN_PROGRESS; createDate = new Date();
eventsManager.handle(orderEventsFactory.orderSubmitted(this)); }
public Iterator<OrderItem> getOrderItems(){
return items.iterator();
} }
@Entity
public class Order{
@Id private OrderId id;
@OneToMany private List<OrderItem> items = new ArrayList<OrderItem>();
private BigDecimal sum = new BigDecimal(0);
//.... status, createDate, rebatePolicy, productRepository,...
public void add(ProductId id, int quantity){ Product p = productRepository.load(id);
OrderItem oi = orderItemFactory.build(p, quantity, rebatePolicy); items.add(oi);
sum = sum.add(oi.getCost()); }
public void submit(){
if (status != Status.NEW)
throw new InvalidStateException(); status = Status.IN_PROGRESS;
createDate = new Date();
eventsManager.handle(orderEventsFactory.orderSubmitted(this)); }
public Iterator<OrderItem> getOrderItems(){
return items.iterator(); }
Builder Design Pattern
●Use factory when creation of Entity/Aggregate is complicated ●Factory prevents from invalid Domain Object
● if “raw material” is invalid than factory vetoes ●Options
● add factory method to closely related objects ● if creating VO consider Singleton
●Architectural aspects
● when creating ORM Entities (out of IoC containter control) ● than inject dependencies manually in factory method
Repository
●Storage abstraction of objects (Entity/Aggregate) of some type
●Encapsulates DB access
● decoupling domain and technical stuff
●Retrieves objects by
● identity
● business criteria (in business Use Cases)
AOP Transactions
Application Service
OrdersRepository InvoicesRepository
OrmOrdersRepositoryImpl OrmInvoicesRepositoryImpl
Persistence Unit + Transactions Manager
Policy
●
Process as a Domain Object
● makes it explicit
● extends Ubiquitous Language
●
Simply: Strategy Design Pattern
●
Useful when there is more than one way to
carry out a process
● if process variations can be covered
by common interface
public class Order{
//FIXME: change to BigDecimal private double totalCost;
private TaxPolicy taxPolicy;
private RebatePolicy rebatePolicy; public void submit(){
totalCost-=rebatePolicy.calculateRebate(this); totalCost-=taxPolicy.calculateTax(this); ... } } <<interface>> TaxPolicy calculateTax(order) PolishTaxPolicy UKTaxPolicy
Policy example
IoC Container
Policy injection
getOrderFactory return OrderFactory OrderFactory PolishTaxPolicy setTaxPolicy@Component
public class OrderFactory{
private TaxPolicy taxPolicy;
private RebatePolicy rebatePolicy; @Autowired
public OrderFactory(TaxPolicy tp, RebatePolicy rp){ this.taxPolicy = tp; this.rebatePolicy=rp;
}
public Order createOrder(...){ Order o = new Order(...);
…
o.setTaxPolicy(taxPolicy);
o.setRebatePolicy(rebatePolicy); }
@Component(„taxPolicy”)
public class PolishTaxPolicy implements TaxPolicy{ ...
}
<bean id="taxPolicy" class="x.y.PolishTaxPolicy">
@Configuration
public class PolicyFactories{
@Bean
public TaxPolicy taxPolicy() { return ... ;
} }
Policy – common pattern
Change classic thinking
● noun – class ● verb - method
Activity is an object
● method is just a signal to execute it
Unleash OO techniques
● polymorphic execution ● re-usability
Extension and Testability
● Extension without modification ● Strategy implies high cohesion
● Single policy can be tested
● Aggregate can be tested using policy stub/mock
Aggregate <<interface>> Strategy Concrete Business Impl Stub/Mock Test Impl Test Test
Events
●
Business Event is a signal from Aggregate
●Aggregate is highly decoupled
● does not know about receiver type
@Entity
public class Order{
public void submit(){
if (status != Status.NEW)
throw new InvalidStateException();
status = Status.IN_PROGRESS; createDate = new Date();
eventsManager.handle(orderEventsFactory.orderSubmitted(this)); }
}
@Entity
public class Order{
public void submit(){
if (status != Status.NEW)
throw new InvalidStateException(); status = Status.IN_PROGRESS;
createDate = new Date();
eventsManager.handle(orderEventsFactory.orderSubmitted(this));
} }
What are Events for?
●
Decouple additional behavior
● adding new behavior without domain modification ● strong form of Inversion of Control
●
Collect state change (if we need to keep track
of entity changes history) – Events Sourcing
● events can signalize change
●
Asynchronous invocation
● When fast response is needed ● Distributed DDD...
Specification
●
Model of rules
●
Makes rules explicit
●
Can be used for Entity/Aggregate validation or
selection
public interface InvoiceSpecyfication{
public Collection<InvoiceProblem> check(Invoice i) }
public interface InvoiceSpecyfication{
public Collection<InvoiceProblem> check(Invoice i) }
public class CombinedInvoiceSpecyfication
implements InvoiceSpecyfication{
private List<InvoiceCriterion> criteria;
public Iterable<InvoiceProblem> check(Invoice i){ Collection<InvoiceProblem> result = new ...;
for (InvoiceCriterion criterion : criteria){ String problem = criterion.validate(i);
if (problem != null){
result.add(new InvoiceProblem(problem));
if (criterion.isCritical())
break;
return result; }}}}
public class CombinedInvoiceSpecyfication
implements InvoiceSpecyfication{
private List<InvoiceCriterion> criteria;
public Iterable<InvoiceProblem> check(Invoice i){ Collection<InvoiceProblem> result = new ...;
for (InvoiceCriterion criterion : criteria){ String problem = criterion.validate(i);
if (problem != null){
result.add(new InvoiceProblem(problem));
if (criterion.isCritical())
break;
return result; }}}}
public interface InvoiceCriterion{
public String validate(Invoice i);
public boolean isCritical(); }
public interface InvoiceCriterion{
public String validate(Invoice i);
public boolean isCritical(); }
Domain Layer (Business Logic) Aggregate Entity (Aggregate root) Entity Value Object business methods Delegate Load Save Business Service <<interface>> Policy
(Strategy Design Pattern)
Application Layer (Application Services, Use Case Agents)
PolicyImpl1 PolicyImpl2
Building Blocks - cooperation
<<interface>> Repository Factory Event Generate Create Delegate Delegate
Comparison to procedural approach
PROCEDURE orderService(o:TOrder); BEGIN ... END; TOrder = RECORD id: integer; ... END;Strategic design
●
Distillation of Domains
●
Defining Bounded Contexts
You can't do everything perfect
●
Not enough knowledge
●
Not enough skilled people
●Not enough time
●
Not enough money
●
Not enough time
●
Not enough money
●
Not enough time
●
Not enough money
●
Not enough time
Welcome to the real world, Neo
Focus on core domain
Core Domain
● Reason why we create system
● Main business features (give advantage to the client)
● Focus intellectual effort – invest time and best people
Supporting Domain
● Additional features – not critical
● Lower quality is acceptable
● Can be rewritten (someday - sure;)
Generic Domain
● Very specific (invoicing, math calculations)
Core Domain Strategies
●
Write Domain Vision Statement
● what brings value?
●
Keep it small
●
Invest the best people
Context
When distinct models are combined, we are in
troubles
● difficult to understand ● unreliable
Grand Unified Theory
Common anti-pattern
● one, big, corporate model ● meaningless
● one word in different contexts mean different thing
– responsibility – behavior
Bounded Context
●
When system grow up – complexity is too high
● can't look at the level of individual objects
●
Define context where model applies
●Context is encapsulated
● communicates with outer word via interface ● inner business model is hermetic
● therefore it can evolve -changes don't spread
Contexts Strategies
●
Common Core Context can be defined
● Shared Kernel
●
Decouple distinct Domain Contexts
● Core (business)
Anti-corruption Strategies
Our brand new shiny nice system
Ugly legacy system Anti-corruption Layer Facade Adapter Service
Main rule
Platform, framework or technical architecture
should not influence arena of domain model
Useful techniques already
discussed
●
ORM - aggregates
●Inversion of Control
● Dependency Injection - policies ● Events
● Aspect Oriented Programming – transactions (also
security) over application layer that impact Repositories
●
Design Patterns
Command-query Separation Paradigm
●Method should play one of the following roles
● command – executes some logic
● query – return data
●
Multiple query should not affect the answer
●Eliminates side effect
●
Class level violation
●
System level violation
●
Add an Order and return list of all orders
●it's GUI functionality
●
but server API should offer 2 methods
private int x;
public int incrementAndReturn(){ x++;
return x; }
private int x;
public int incrementAndReturn(){ x++;
return x; }
(G)UI Business Logic Application Logic Command Facade ORM + JDBC Query DTO Repository/DAO
Command-query Responsibility Segregation
CqRS Characteristic
●
Processing C and Q is often asymmetrical
● Should be scaled separately
(G)UI Business Logic Application Logic Command Data provider/Finder Query DTO Repository/DAO DB 1 DB 2 Event (asynch) Events Bus + Event handlers Change
public class SearchDocumentsQuery implements Serializable{
private Status status;
private Date epiryDate;
private String[] titleWords;
private String[] contentWords; //Getters and setters/constructor }
public class SearchDocumentsQuery implements Serializable{
private Status status;
private Date epiryDate;
private String[] titleWords;
private String[] contentWords; //Getters and setters/constructor }
public class DocumentQueries /*implements SomeInterface*/{
public List<Document> search(SearchDocumentsQuery query){ //ORM return entities – simple case
}
public List<DocumentDTO> search(SearchDocumentsQuery query){ //SQL return UseCase relevant Data Transfer Object
} }
public class DocumentQueries /*implements SomeInterface*/{
public List<Document> search(SearchDocumentsQuery query){ //ORM return entities – simple case
}
public List<DocumentDTO> search(SearchDocumentsQuery query){ //SQL return UseCase relevant Data Transfer Object
} }
public class SearchDocumentsQuery implements Serializable{
private Status status;
private Date epiryDate;
private String[] titleWords;
private String[] contentWords; //ONLY getters
public SearchDocumentsQuery expired(){ status = Status.ACTIVE;
expiryDate = new Date();
return this; }
public SearchDocumentsQuery contains(String phrase){ String[] words = phrase.split(" ");
titleWords = words; contentWords = words;
return this; }
}
public class SearchDocumentsQuery implements Serializable{
private Status status;
private Date epiryDate;
private String[] titleWords;
private String[] contentWords; //ONLY getters
public SearchDocumentsQuery expired(){ status = Status.ACTIVE;
expiryDate = new Date();
return this; }
public SearchDocumentsQuery contains(String phrase){ String[] words = phrase.split(" ");
titleWords = words; contentWords = words; return this; } }
Query a'la DSL
public class OrderProductCommand implelents Serializable{ private int productId;
private int quantity;
//getters and constructor }
public class OrderProductCommand implelents Serializable{ private int productId;
private int quantity;
//getters and constructor }
public class Basket /*implements SomeInterface*/{ private Order order;
public void add(AddProductCommand cmd){
Product prod = productRepository.get(cmd.getProductId()); order.add(prod, cmd.getQuantity());
}
public void submit(SubmitOrderCommand cmd){
order.submit(cmd.getPayment(), cmd.getAddressInfo()); orderRepository.save(order);
eventsManager.orderSubmitted(new OrderSubmittedEvent(order)); }
}
public class Basket /*implements SomeInterface*/{ private Order order;
public void add(AddProductCommand cmd){
Product prod = productRepository.get(cmd.getProductId()); order.add(prod, cmd.getQuantity());
}
public void submit(SubmitOrderCommand cmd){
order.submit(cmd.getPayment(), cmd.getAddressInfo()); orderRepository.save(order);
eventsManager.orderSubmitted(new OrderSubmittedEvent(order)); }
}
public class OrderProductCommand extends Command{...}
public class OrderProductCommand extends Command{...}
public interface Handler<T> {
void handleMessage(T message) throws Exception; }
public interface Handler<T> {
void handleMessage(T message) throws Exception; }
public class AddProductHandler
implements Handler<AddProductCommand>{ private ProductRepository repository;
public void handleMessage(AddProductCommand message) { //...
} }
public class AddProductHandler
implements Handler<AddProductCommand>{ private ProductRepository repository;
public void handleMessage(AddProductCommand message) { //...
} }
public class CommandBus{
public void handle(Command cmd)
Collection<Handler<?>> matchedHandlers = handlers.get(message.getClass());
for (Handler handler : matchedHandlers){
//prepare environment (transactions, security, //inject current user, etc)
handler.handleMessage(cmd); }
//may generate message }
}
public class CommandBus{
public void handle(Command cmd)
Collection<Handler<?>> matchedHandlers = handlers.get(message.getClass());
for (Handler handler : matchedHandlers){
//prepare environment (transactions, security, //inject current user, etc)
handler.handleMessage(cmd); }
//may generate message
} }
Event Sourcing
●
If state of Aggregate at any given time is
needed to be rebuild
● not the same as Logs
●
"Inner" events
● represents current state of Aggregate ● events are stored in persistence layer
● Aggregate can be recreated to given state (point in
Temporal Object Pattern
Alternative to Event Sourcing
●whole state of Aggregate version persisted
●newest version is always available – no computation
Is DDD right for me?
●
Aspects of personality
●
Aspects of business domain itself
●Aspects of project nature
Sometimes it's all about just collecting data
(with nice GUI)
DDD Prerequisites
●
Domain is not trivial
●
Team has experience and interest in OOP/OOD
●You have access to domain experts
Advanced data models
●
Model Archetype – analytical “design pattern”
● standard
● "model component" that can be taken off the shelf,
customized, and instantiated in your own models
● adaptable – can be trimmed to fit actual
requirements
● extensible – can be extended to fit new
requirements
●
Catalog of models:
● Company structure and relationships, customers,
products, inventory, orders, scientific stuff (laboratory),...
How to model People and
Organizations?
Person Employee User ClientInheritance is not a good idea to model roles:P Company
Party
General Idea
Example
Model
Details of Party
Person is a Party
Organization is a Party
Customer is a Party
Customer is a different story...
Behavior Driven Development
●
Agile software development technique
●Encourages collaboration between
● developers, QA
● non-technical or business participants
●
BDD focuses on exposing internal logic
(typically business rules) to review by
stakeholders.
●
Native language in combination with the
ubiquitous language of DDD
Data Context Interaction
●
New programming paradigm
● needs dynamic language syntax (mixins, traits)
●
Trygve Reenskaug
● co-inventor of Object Oriented ● inventor of MVC
●
Mainstream OO languages
● are not Object Oriented
Use Case flow and global algorithm Classes Classes Classes Classes Classes
Data
● core
● basically dumb ● may contain basic
responsibility Interactions ● business responsibility ● operates on data Context
● Whole Use Case ● or just few steps ● adds meaning to
the data
Full working Objects
● makes sense only in context ● methods implies by current role
DDD summary
●
No rocket science
●
“Just” rational usage of OOA and OOD
●
Pragmatic approach to complexity of business
logic
●
Most important things:
● Ubiquitous Language ● Strategic Design
●
Building Block are
not
the most important
thing:P
Code should mean something
Sławomir Sobótka
Dziękuję za uwagę
więcej...
http://art-of-software.blogspot.com slawomir.sobotka@bottega.com.pl