Domain Driven Design
Morten Mertner Senior Consultant, Teknologisk Institut - Architect and Software Developer (C#) - Teacher - Speaker (conferences and gatherings) Certifications - MSCD.NET - MCT Open Source - Gentle.NET (object persistence framework / object-relational mapper) - MbUnit (unit test framework)
Agenda Domain Driven Design - Introduction - Patterns - Entity and ValueObject - Repository - Specification - Extensions - Factory - Conclusion - Domain Models and SOA
TransactionScript vs Domain Model Transaction Script - procedural - simple and easy to understand - minimal performance overhead - often results in cut & paste reuse (hard to maintain) - does not scale to complicated tasks Domain Model - logic organised in units that cooperate to solve business operations and rules - concepts in problem domain are modelled as classes - true object-orientation
TransactionScript vs Domain Model
Introduction Patterns - Entity - ValueObject - Repository - Specification
DDD Entity Entity - uniquely identifyable (has identity) - equality comparison uses identity (not attribute values) - usually extended longevity Examples - Product - Customer - Order
Example: Order = Entity class Order { Customer customer; List<OrderLineItem> lineitems; public Order( Customer customer ) { this.customer = customer; lineitems = new List<OrderLineItems>(); public void AddLineItem( OrderLineItem li ) { lineitems.add(li); public void RemoveLineItem( OrderLineItem li ) { lineitems.remove(li); public IList<OrderLineItem> LineItems{ get { return lineitems; public float GetTotalPrice() { float result = 0; foreach( OrderLineItem li in lineitems ) { result += li.price; return result;
DDD ValueObject ValueObject - no identity - equality comparison uses attribute values - describes entities - should be immutable Example - OrderLineItem
Example: OrderLineItem = ValueObject class OrderLineItem { int quantity; Product product; float price; public OrderLineItem( Product p, int quantity, float price ) { this.product = p; this.quantity = quantity; this.price = price; public int Quantity { get { return quantity; public Product Product { get { return product; public float Price { get { return price;
DDD Entity & ValueObject
DDD Repository Repository - responsible for object persistence - interface similar to collection class - creates an illusion of all objects being in memory Example - CustomerRepository - ProductRepository - OrderRepository
Repository public interface IRepository<T> { void Add( T newelement ); void Remove( T element ); void Remove( int oid ); T FindByOID( int oid ); IList<T> Select(... ); public class AbstractRepository<T> : IRepository<T> { // implemenation code public class CustomerRepository : AbstractRepository<Customer> { public class OrderRepository : AbstractRepository<Order> { public class ProductRepository : AbstractRepository<Product> {
DDD Repository
DDD Specification Specification - business rule (encapsulated in an object) Purpose - data query - validation
DDD Specification interface ISpecification<T> { bool IsSatisfiedBy( T domainobject ); class GoldCustomerSpecification : ISpecification<Customer> { public bool IsSatisfiedBy( Customer customer ) { return customer.totalorderamount > 10000;
DDD Repository + Specification public interface IRepository<T> { void Add( T newelement ); void Remove( T element ); void Remove( int oid ); T FindByOID( int oid ); IList<T> Select( ISpecification<T> spec ); public class AbstractRepository<T> : IRepository<T> { // implemenation code public class CustomerRepository : AbstractRepository<Customer> { public class OrderRepository : AbstractRepository<Order> { public class ProductRepository : AbstractRepository<Product> {
Specification with Persistence Support interface ISpecification<T> { bool IsSatisfiedBy( T domainobject ); bool HasQuerySupport { get; string querystring { get; // ADO.NET interface ISpecification<T> { bool IsSatisfiedBy( T domainobject ); bool HasQuerySupport { get; Expression Criteria { get; // ORM (Gentle.NET 2.0)
RepositoryFactory RepositoryFactory - avoids coupling between entities in data access code - clean encapsulation of all database code - database provider independence (ADO.NET) - ORM independence
RepositoryFactory public interface IRepositoryFactory { IRepository<T> GetRepository<T>( T entitytype ); public class RepositoryFactory { IRepository<T> GetRepository<T>( T entitytype ) { return new GentleRepository<T>();
Usage // create new order Order order = new Order(); order.addlineitem( new OrderLineItem( product, 5, 50.0 ) ); OrderRepository orderrepository = RepositoryFactory.GetRepository<Order>(); orderrepository.add( order ); // find all gold customers CustomerRespository cr = RepositoryFactory.GetRepository<Customer>(); IList<Customer> goldies = cr.select( new GoldCustomerSpecification() ); // check whether a given customer is a gold customer Customer customer = ; if( new GoldCustomerSpecification().IsSatisfiedBy( customer ) ) {
Domain Models Advantages - provides sufficient abstraction to deal with complex problem domains - scales to very large code bases - common language between developers and domain experts - once mastered it is a powerful tool you rarely develop any other way Disadvantages - mapping domain objects to a relational database is difficult and timeconsuming (unless you use an ORM tool) - takes skill and effort to understand, experience to master
Domain Models and SOA
Resources Eric Evans Domain Driven Design http://domaindrivendesign.org/book/ Martin Fowler Patterns of Enterprise Application Architecture http://martinfowler.com/books.html#eaa Steve Eichert (blog) http://steve.emxsoftware.com/domain+driven+design Kim Harding Christensen, EOS
Implementation Gentle.NET 2.0 - open source ORM - working implementation ISpecification - ISpecification is called IFilter - IFilter supports Combine/Remove/Sort (both in-memory and data) - IObjectIdentity used to abstract object identifier (here we used int)
Implementation Source Code http://www.mertner.com/svn/gentle/gentle/ SourceBranches/2.0.0/Gentle.Persistence/Client/Repository/*.cs http://www.mertner.com/fisheye Documentation http://www.mertner.com/confluence/display/gentle/design+recommendations
Questions?
Thoughts & Unsolved Problems Transactions - transaction initiation and participation - cross-repository transactions - automatic enlisting Generics - make it difficult to put repository in base class - slightly more cumbersome than using non-generic classes RelationFilter - requires ability to modify not just the where-clause of queries