360 likes | 563 Views
Designing High Performance, Persistent Domain Models. Udi Dahan – The Software Simplist Enterprise Development Expert & SOA Specialist. Session Code: <Session code>. What most architecture looks like today. But where do business rules go?. Here?. Here?. Here?. Here?. Here?. Here?.
E N D
Designing High Performance,Persistent Domain Models Udi Dahan – The Software Simplist Enterprise Development Expert & SOA Specialist Session Code: <Session code>
But where do business rules go? Here? Here? Here? Here? Here? Here?
The book that changed everything ISBN 0321127420
The Domain Model Pattern Methods Properties Events
Independent of all concerns DB UI Communications
Deploy On All Tiers it’s just a bunch of DLLs – or even just one DB SQL CLR
Domain Models are made of POCOs public class Product { // properties // methods } public class Customer { // properties // methods } public class Order { // properties // methods } public class OrderLine { // properties // methods }
Highly Testable Register for events Test Call a method Check properties & event args
Persisting Domain Models to the DB ? But who’s in charge Object Relational Mapping DB
Service Layer Encapsulates ORM Service Layer Object Relational Mapping Manages connections and transactions DB
Service Layer Code publicvoidMake_Customer_Preferred(Guidid) { using (ISession s = ORM.Open_Session()) using (ITransactiontx = s.Begin_Transaction()) { Customerc = s.Get<Customer>(id); c.Make_Preferred(); tx.Commit(); } } Solves Concurrency
Domain Model Sample Lazy Loading public class Customer { publicvoidMake_Preferred() { foreach(Orderointhis.UnshippedOrders) foreach(Orderlineolino.OrderLines) ol.Discount(10.Percent); } }
Lazy Loading – load what you use Customer Order ? OrderLine Why load Orders and Order Lines when you just want to add a new order
Dangers of Lazy Loading • public class Customer • { • publicvoidMake_Preferred() • { • foreach(Order o inthis.UnshippedOrders) • foreach(Orderlineolino.OrderLines) • ol.Discount(10.Percent); • } • } DB
Minimize Roundtrips: Eager Fetching • publicvoidMake_Customer_Preferred(Guid id) • { • using (ISession s = ORM.Open_Session()) • using (ITransactiontx = s.Begin_Transaction()) • { • var c = s.Get<Customer>(id); • c.Make_Preferred(); • tx.Commit(); • } • } Need to get all data (Orders, Orderlines, etc) No need to hit DB here
Easier said than done The same entity can be loaded many ways Making a customer “preferred” Adding an Order Customer Customer Order OrderLine
I want it all Pretty please?
Using interfaces to differentiate roles IMakeCustomerPreferred IAddOrdersToCustomer void MakePreferred(); void AddOrder(Order o); Customer
Service Layer Specifies Role • publicvoidMake_Customer_Preferred(Guid id) • { • using (ISession s = ORM.Open_Session()) • using (ITransactiontx = s.Begin_Transaction()) • { • var c = s.Get<IMakeCustomerPreferred>(id); • c.Make_Preferred(); • tx.Commit(); • } • }
Fetching Strategy Per Role IMakeCustomerPreferred void MakePreferred(); MakeCustomerPreferredFetchingStrategy : IFetchingStrategy<IMakeCustomerPreferred> string Strategy { get { return “UnshippedOrders, OrderLines”; } } IFetchingStrategy<T>
ORM Uses Fetching StrategyIFetchingStrategy<IMakeCustomerPreferred> • publicvoidMake_Customer_Preferred(Guid id) • { • using (ISession s = ORM.Open_Session()) • using (ITransactiontx = s.Begin_Transaction()) • { • var c = s.Get<IMakeCustomerPreferred>(id); • c.Make_Preferred(); • tx.Commit(); • } • }
New Rule for “Add Order” Yes > $100K 10 % Discount Existing Orders No
Eager fetching brings a lot of data Rule Implementation • public class Customer • { • publicvoidAdd_Order(Order o) • { • float sum = Total(this.ShippedOrders); • if (sum > 100.Thousand) • o.Discount(10.Percent); • this.Orders.Add(o); • o.Customer = this; • } • }
Member replaces iteration Σ sumOfShippedOrders Update ORM Q Persistent Update when orders ship + Use when adding orders DB
New Rule Implementation No Fetching Strategy Needed • public class Customer • { • publicvoidAdd_Order(Order o) • { • if (this.sumOfShippedOrders > 100.Thousand) • o.Discount(10.Percent); • this.Orders.Add(o); • o.Customer = this; • } • } this.sumOfShippedOrders
Joins hurt scalability: De-NormalizeVertically partition tables to increase throughput Keep the same interface, change internals ORM DB Update mapping & schema
But some rules have to be in the DBdon’t they? No Duplicate user names @ No Duplicate email addresses Unique Constraints work
Memory of a single server can hold: • 64 million user names = ~ 2 GB • 16 char username @ • 48 million emails = ~ 2 GB • 40 char email Partition data load across servers for much more
Low latency, high throughputand maintainable Keeps the boss happy
Thank you Udi Dahan – The Software Simplist Enterprise Development Expert & SOA Specialist email@UdiDahan.com www.UdiDahan.com