poeaa by example

149
Paulo Sousa [email protected] InstitutoSuperior de Engenharia do Porto

Upload: paulo-sousa

Post on 28-Nov-2014

8.098 views

Category:

Technology


4 download

DESCRIPTION

Patterns of Enterprise Application Architecture

TRANSCRIPT

  • 1. Paulo Sousa [email protected] Instituto Superior de Engenharia do Porto
  • 2. Introduction Enterprise Applications Sample problem Business entities Business logic and data access Some improvements Sample application Patterns for distributed applications Synopsis Conclusions
  • 3. Part 1
  • 4. Each pattern describes a problem that occurs over and over again in our environment and then describes the core of the solution to that problem in such a way that you can use this solution a million times over without ever doing it the same way twice. Christopher Alexander (architect)
  • 5. A Software Design Pattern names, abstracts, and identifies the key aspects of a common design structure that make it useful for creating a reusable object-oriented design. Design Patterns-Elements of Reusable Object-oriented Software, Gamma et al. (Gang of Four)
  • 6. a set of best practices a typified solution to a problem in a given context a way to facilitate communication found not invented
  • 7. Patterns are half-baked Martin Fowler
  • 8. No direct code reuse Pattern overload Experience-based validation Hard work integrating patterns in the development process 8
  • 9. name Contributes to the pattern vocabulary synopsis Short description of the problem the pattern will solve. forces Requirements, considerations, or necessary conditions solution The essence of the solution counter forces Reasons for not using the pattern. related patterns Possible alternatives in the design. 9
  • 10. GoF Gang of Four POSA Pattern-Oriented Software Architecture PoEAA Patterns of Enterprise Application Architecture CJP Core J2EE Patterns ESP Enterprise Solution Patterns using Microsoft .NET 10
  • 11. Part 2
  • 12. Critical functionality Large quantity of concurrently accessed data Large number of screens Integration Conceptual dissonance Complex (ilogic) business rules
  • 13. [Software Architecture is] the fundamental organization of a system, embodied in its components, their relationships to each other and the environment, and the principles governing its design and evolution. ANSI/IEEE Std 1471-2000, Recommended Practice for Architectural Description of Software-Intensive Systems
  • 14. fonte: Application Architecture for .NET: designing applications and Services
  • 15. Layers fonte: Core J2EE Patterns
  • 16. fonte: Application Architecture for .NET: designing applications and Services
  • 17. How to represent the business entities? How to persist its state? How to code the Business logic? how to guarantee data coherence? How to handle application distribution?
  • 18. Part 3
  • 19. Revenue recognition Three different products Word processors, databases, spreadsheets Different payment rules WP all at once (t) DB 3 payments: t, t+30, t+60 SS 3 payments: t, t+60, t+90 (From Martin Fowlers PoEAA book)
  • 20. Customer Contract Product 1 * + name + revenue + name * + dateSigned 1 1 WordProcessor SpreadSheet * RevenueRecognition + dateRecognition DataBase + amount
  • 21. table TRevenueRecognitions + Column ID : int + Column contractID : int + Column dateRecognition : date + Column amount : currency table table TContracts TProducts table + Column ID : int TCustomers + Column ID : int + Column productID : int + Column type : varchar + Column ID : int + Column customerID : int + Column name : varchar + Column name : varchar + Column revenue : currency + Column dateSigned : date
  • 22. public interface IRevenueRecognition { void CalculateRevenueRecognitions(int contractID); Money RecognizedRevenue(int contractID, DateTime asOf); object GetContracts(); object GetCustomers(); object GetProducts(); }
  • 23. Part 4
  • 24. How to represent: One entity, e.g. Customer? Collections, e.g., List of customers? Networked/complex objects, e.g., production plan? Structure and behaviour Structure + behaviour? Structure with behaviour? Behaviour (and attributes)? Persistence structure conceptual structure?
  • 25. Custom classes XML DataSet (.net) / ResultSet (JDBC)
  • 26. Custom classes IList / List XML DataSet (.net) / ResultSet (JDBC)
  • 27. User defined code with members for the entitys attributes Product + Type : int + Name : string + ID : int
  • 28. Product Contract Customer + Type : int + Revenue : decimal + Name : string + DateSigned : DateTime + Name : string + ID : int + CustomerID : int + ID : int + ProductID : int + ID : int * + RevenueRecognitions RevenueRecognition + DateRecognition : DateTime + Amount : decimal + ContractID : int + ID : int
  • 29. An XML document (or string) representing an entitys structure 123SuperWriter 7.3Word Processor
  • 30. A DataSet (or derived class) to hold tabular data (eventually directly from a data source)
  • 31. A class implementing the ResultSet interface to hold tabular data (eventually directly from a data source)
  • 32. A library collection class (e.g. LinkedList) LinkedList
  • 33. Part 5
  • 34. Table oriented BLL Table Module DAL Table Data Gateway Object oriented BLL Domain Model Active Record DAL BLL Data Mapper
  • 35. Part 5.1
  • 36. UI Uses the platforms ResultSet for sharing entity data table module beteween layers BLL table data gateway DAL
  • 37. Pattern A single instance that handles the business logic for all rows in a database table or view fonte: Patterns of Enterprise Application Architecture
  • 38. Contract + Contract ( ) + RecognizedRevenue ( [in] contractID : int , [in] asOf : DateTime ) : Money + CalculateRevenueRecognitions ( [in] contractID : int ) + GetContracts ( ) : DataSet + GetContractsByProduct ( [in] productID : int ) : DataSet + GetContractsByCustomer ( [in] customerID : int ) : DataSet Customer + Customer ( ) + GetCustomers ( ) : DataTable Product + Product ( ) + GetProducts ( ) : DataTable
  • 39. Pattern An object that acts as a Gateway to a database table. One instance handles all the rows in the table Business entity fonte: Patterns of Enterprise Application Architecture
  • 40. CustomerGateway + CustomerGateway ( ) + GetCustomers ( ) : DataTable ContractGateway ProductGateway + ContractGateway ( ) + InsertRecognition ( [in] contractID : int , [in] recognitionDate : DateTime , [in] amount : decimal ) : int + ProductGateway ( ) + GetContracts ( ) : DataSet + GetProducts ( ) : DataTable + GetContractByID ( [in] contractID : int ) : DataSet + GetContractsByProduct ( [in] productID : int ) : DataSet + GetContractsByCustomer ( [in] customerID : int ) : DataSet BaseGateway # property CurrentTransaction : OleDbTransaction - CONNSTR : string = @quot;Provider=...quot; + BaseGateway ( ) # GetConnection ( [in] open : bool ) : OleDbConnection # ExecuteQuery ( [in] cnx : OleDbConnection , [in] sql : string ) : DataSet # ExecuteNonQuery ( [in] cnx : OleDbConnection , [in] sql : string ) : int # ExecuteNonQuery ( [in] tx : OleDbTransaction , [in] sql : string ) : int # ExecuteNonQuery ( [in] tx : OleDbTransaction , [in] cmd : OleDbCommand ) : int + BeginTransaction ( ) 42 + CommitTransaction ( ) ISEP/IP + RoolbackTransaction ( ) # get CurrentTransaction ( ) : OleDbTransaction P
  • 41. / User : Actor1 / GUI / ClassifierRole1 : Contract / ClassifierRole2 : Contract Gateway 1 : Form_Load 2 : GetContracts ( ) 3 : GetContracts ( ) 4 : GetConnection ( open ) 5 : ExecuteQuery ( cnx , sql )
  • 42. / User : Actor1 / GUI / ClassifierRole1 : Contract 1 : btnCalcRevenues_Click 2 : CalculateRevenueRecognitions ( contractID ) 3 : new / ClassifierRole2 : Contract Gateway 4 : GetContractByID ( contractID ) 5 : BeginTransaction ( ) foreach calculated 6 : InsertRecognition ( contractID , recognition recognitionDate , amount ) 7 : CommitTransaction ( )
  • 43. Returns a join of public void CalculateRevenueRecognitions(int contractID) { TContracts and DAL.ContractGateway dal = new DAL.ContractGateway(); DataSet ds = dal.GetContractByID(contractID); TProducts string prodType = (string)ds.Tables[0].Rows[0][quot;typequot;]; decimal totalRevenue = (decimal)ds.Tables[0].Rows[0][quot;revenuequot;]; DateTime recDate = (DateTime)ds.Tables[0].Rows[0][quot;dateSignedquot;]; dal.BeginTransaction(); switch (prodType) { case quot;PTquot;: dal.InsertRecognition(contractID, recognitionDate, totalRevenue); break; case quot;FCquot;: decimal[] allocs = Money.Allocate(totalRevenue, 3); dal.InsertRecognition(contractID, recDate, allocs[0]); dal.InsertRecognition(contractID, recDate.AddDays(60), allocs[1]); dal.InsertRecognition(contractID, recDate.AddDays(90), allocs[2]); break; case quot;BDquot;: decimal[] allocs = Money.Allocate(totalRevenue, 3); dal.InsertRecognition(contractID, recDate, allocs[0]); dal.InsertRecognition(contractID, recDate.AddDays(30), allocs[1]); dal.InsertRecognition(contractID, recDate.AddDays(60), allocs[2]); break; Explicit transaction control } dal.CommitTransaction(); / }
  • 44. public int InsertRecognition(int contractID, DateTime recognitionDate, decimal amount) { OleDbCommand sqlcmd = new OleDbCommand( quot;INSERT INTO TRevenueRecognitions (contractID, dateRecognition, amount) VALUES (?, ?, ?)quot;, CurrentTransation.Connection, CurrentTransation ); sqlcmd.Parameters.Add(quot;@cidquot;, contractID); sqlcmd.Parameters.Add(quot;@dtquot;, recognitionDate); sqlcmd.Parameters.Add(quot;@amtquot;, amount); return ExecuteNonQuery(CurrentTransation, sqlcmd); }
  • 45. Part 5.2
  • 46. UI These classes only have attributes (no business logic- table module Entities related behaviour) BLL table data gateway DAL
  • 47. Product Contract Customer + Type : int + Revenue : decimal + Name : string + DateSigned : DateTime + Name : string + ID : int + CustomerID : int + ID : int + ProductID : int + ID : int * + RevenueRecognitions RevenueRecognition + DateRecognition : DateTime + Amount : decimal + ContractID : int + ID : int
  • 48. Contract + Contract ( ) + RecognizedRevenue ( [in] contractID : int , [in] asOf : DateTime ) : Money + CalculateRevenueRecognitions ( [in] contractID : int ) + GetContracts ( ) : IList + GetContractsByProduct ( [in] productID : int ) : IList + GetContractsByCustomer ( [in] customerID : int ) : IList Customer + Customer ( ) + GetCustomers ( ) : IList Product + Product ( ) + GetProducts ( ) : IList
  • 49. CustomerGateway ContractGateway + CustomerGateway ( ) + GetCustomers ( ) : IList + ContractGateway ( ) - CreateCustomerObject ( [in] r : DataRow ) : Customer + InsertRecognition ( [in] contractID : int , [in] recognitionDate : DateTime , [in] amount : decimal ) : int + GetContracts ( ) : IList + GetContractByID ( [in] contractID : int ) : Contract ProductGateway + GetContractsByProduct ( [in] productID : int ) : IList + GetContractsByCustomer ( [in] customerID : int ) : IList + ProductGateway ( ) - CreateContractObject ( [in] r : DataRow ) : Contract + GetProducts ( ) : IList - CreateRevenueRecognitionObject ( [in] r : DataRow ) : RevenueRecognition - CreateProductObject ( [in] r : DataRow ) : Product BaseGateway # property CurrentTransaction : OleDbTransaction - CONNSTR : string = @quot;Provider=...quot; + BaseGateway ( ) # GetConnection ( [in] open : bool ) : OleDbConnection # ExecuteQuery ( [in] cnx : OleDbConnection , [in] sql : string ) : DataSet # ExecuteNonQuery ( [in] cnx : OleDbConnection , [in] sql : string ) : int # ExecuteNonQuery ( [in] tx : OleDbTransaction , [in] sql : string ) : int # ExecuteNonQuery ( [in] tx : OleDbTransaction , [in] cmd : OleDbCommand ) : int + BeginTransaction ( ) + CommitTransaction ( ) 51 + RoolbackTransaction ( ) ISEP/IP # get CurrentTransaction ( ) : OleDbTransaction P
  • 50. / User : Actor1 / GUI / ClassifierRole1 : Contract 1 : Form_Load 2 : GetContracts ( ) / ClassifierRole2 : Contract 3 : new Gateway 4 : GetContracts ( ) 5 : GetConnection ( open ) 6 : ExecuteQuery ( cnx , sql ) 7 : new / IList 8 : new / ClassifierRole3 : Contract foreach row returned 9 : Add 52 ISEP/IP P
  • 51. / GUI / bll : Contract Sequence 1 : btnCalcRevenues_click 2 : CalculateRevenueRecognitions ( CalculateRevenues contractID ) 3 : new / dalC : ContractGateway 4 : GetContractByID ( contractID ) 5 : CreateContractObject ( r ) 6 : new / c : Contract 7 : new / dalP : ProductGateway 8 : GetProductByID ( productID ) 9 : CreateProductObject ( r ) 10 : new / p : Product 11 : BeginTransaction ( ) foreach calculated revenue 12 : InsertRecognition ( contractID , recognitionDate , amount ) 13 : ExecuteNonQuery ( tx , cmd ) 14 : CommitTransaction ( )
  • 52. public void CalculateRevenueRecognitions(int contractID) { DAL.ContractGateway dalC = new DAL.ContractGateway(); Entities.Contract c = dalC.GetContractByID(contractID); DAL.ProductGateway dalP = new DAL.ProductGateway(); TM.Entities.Product p = dalP.GetProductByID(c.ProductID); dalC.BeginTransaction(); switch (p.Type) { case quot;PTquot;: dalC.InsertRecognition(contractID, c.DateSigned, c.Revenue); break; case quot;FCquot;: decimal[] alcs = Money.Allocate(c.Revenue, 3); dalC.InsertRecognition(contractID, c.DateSigned, allocs[0]); dalC.InsertRecognition(contractID, c.DateSigned.AddDays(60), alcs[1]); dalC.InsertRecognition(contractID, c.DateSigned.AddDays(90), alcs[2]); break; case quot;BDquot;: decimal[] alcs = Money.Allocate(c.Revenue, 3); dalC.InsertRecognition(contractID, c.DateSigned, allocs[0]); dalC.InsertRecognition(contractID, c.DateSigned.AddDays(30), alcs[1]); dalC.InsertRecognition(contractID, c.DateSigned.AddDays(60), alcs[2]); break; } dalC.CommitTransaction(); }
  • 53. Part 5.3
  • 54. GUI domain model + active record BLL + DAL
  • 55. Pattern An object model of the domain that incorporates both behavior and data fonte: Patterns of Enterprise Application Architecture
  • 56. Pattern An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data fonte: Patterns of Enterprise Application Architecture 58
  • 57. Can be divided: Classes (a) ActiveRecord BLL/DAL (b) DBHelper ActiveRecord # myID : int # property CurrentTransaction : OleDbTransaction Product - CONNSTR : string = @quot;Provider=...quot; + property ID : int + property Type : string - _name : string + Save ( ) - _type : string + ActiveRecord ( ) 0..1 # GetConnection ( [in] open : bool ) : OleDbConnection + Product ( ) - _Product # ExecuteQuery ( [in] sql : string ) : DataSet # Product ( [in] row : DataRow ) # ExecuteTransactedQuery ( [in] sql : string ) : DataSet + LoadById ( [in] productID : int ) : Product # ExecuteNonQuery ( [in] sql : string ) : int + LoadAll ( ) : IList # ExecuteTransactedNonQuery ( [in] sql : string ) : int + Save ( ) # ExecuteNonQuery ( [in] tx : OleDbTransaction , [in] cmd : OleDbCommand ) : int + get Type ( ) : string # BeginTransaction ( ) # CommitTransaction ( ) # RoolbackTransaction ( ) # get CurrentTransaction ( ) : OleDbTransaction + get ID ( ) : int Customer # ExecuteTransactedNonQuery ( [in] cmd : OleDbCommand ) : int - Name : string + Customer ( ) - _Customer # Customer ( [in] row : DataRow ) Contract 0..1 + LoadById ( [in] customerID : int ) : Customer + LoadAll ( ) : IList - CustomerID : int + Save ( ) - DateSigned : DateTime - ProductID : int - Revenue : decimal # property Product : Product + Contract ( ) RevenueRecognition # Contract ( [in] row : DataRow ) - RevenueRecognitions + Amount : decimal # Contract ( [in] dsContractAndRecognitions : DataSet ) + RecognizedRevenue ( [in] asOf : DateTime ) : Money * + DateRecognition : DateTime + CalculateRevenueRecognitions ( ) + ID : int + LoadById ( [in] contractID : int ) : Contract + RevenueRecognition ( [in] id : int , [in] dt : DateTime , [in] amt : decimal ) + LoadByProduct ( [in] productID : int ) : IList + RevenueRecognition ( [in] dt : DateTime , [in] amt : decimal ) + LoadByCustomer ( [in] customerID : int ) : IList + LoadAll ( ) : IList + Save ( ) inner private # AddRecognition ( [in] recognitionDate : DateTime , [in] amount : decimal ) class # get Product ( ) : Product
  • 58. DBHelper + property CurrentTransaction : OleDbTransaction ActiveRecord - CONNSTR : string = @quot;Provider=...quot; # myID : int + DBHelper ( ) + property ID : int + GetConnection ( ) + Save ( ) + ExecuteQuery ( ) + ActiveRecord ( ) + ExecuteTransactedQuery ( ) + get ID ( ) + ExecuteNonQuery ( ) + ExecuteTransactedNonQuery ( ) + ExecuteNonQuery ( ) + BeginTransaction ( ) + CommitTransaction ( ) + RoolbackTransaction ( ) Product Contract Customer + get CurrentTransaction ( ) - Name : string - CustomerID : int - Name : string - Type : int - DateSigned : DateTime 0..1 + Customer ( ) - ProductID : int - _Customer inner private class + Product ( ) + LoadById ( ) - Revenue : decimal + LoadById ( ) 0..1 + LoadAll ( ) + LoadAll ( ) - _Product + Contract ( ) + Save ( ) + Save ( ) + RecognizedRevenue ( ) + CalculateRevenueRecognitions ( ) RevenueRecognition + LoadById ( ) + LoadByProduct ( ) - RevenueRecognitions + Amount : decimal + LoadByCustomer ( ) * + DateRecognition : DateTime + LoadAll ( ) + ID : int + Save ( ) + RevenueRecognition ( )
  • 59. / User : Actor1 / GUI : Contract 1 : Form_Load 2 : LoadAll ( ) 3 : ExecuteQuery ( sql ) 4 : new / IList 5 : Contract ( row ) / ClassifierRole2 : Contract 6 : Add foreach returned row
  • 60. Networks of objects E.g. Invoice heading relates to invoice details Invoice details refers to Products Products refers to Suppliers What to do? Load them all into memory? How to disallow multiple in-memory copies 62
  • 61. Pattern An object that doesn't contain all of the data you need but knows how to get it. fonte: Patterns of Enterprise Application Architecture
  • 62. Pattern Ensures that each object gets loaded only once by keeping every loaded object in a map. Looks up objects using the map when referring to them fonte: Patterns of Enterprise Application Architecture 64
  • 63. / User : Actor1 / GUI : Contract Sequence 1 : btnCalcRevenues_Click CalculateRevenues 2 : LoadById ( contractID ) 3 : ExecuteQuery ( sql ) 4 : Contract ( row ) / aContract : Contract 5 : CalculateRevenueRecognitions ( ) 6 : AddRecognition ( recognitionDate , amount ) foreach calculated recognition 7 : Save ( ) 8 : BeginTransaction ( ) 9 : ExecuteTransactedNonQuery ( sql ) 10 : ExecuteTransactedNonQuery ( sql ) foreach RevenueRecognition object in _RevenueRecognitions 11 : CommitTransaction ( )
  • 64. public void CalculateRevenueRecognitions() { switch (this.Product.Type) { case quot;PTquot;: AddRecognition(this.DateSigned, this.Revenue); break; case quot;FCquot;: decimal[] allocsFC = Money.Allocate(Revenue, 3); AddRecognition(DateSigned, allocsFC[0]); AddRecognition(DateSigned.AddDays(60), allocsFC[1]); AddRecognition(DateSigned.AddDays(90), allocsFC[2]); break; case quot;BDquot;: decimal[] allocsBD = Money.Allocate(Revenue, 3); AddRecognition(DateSigned, allocsBD[0]); AddRecognition(DateSigned.AddDays(30), allocsBD[1]); AddRecognition(DateSigned.AddDays(60), allocsBD[2]); break; } }
  • 65. // foreign key private int ProductID; // object pointer private Product _Product; // relationship property protected Product Product { get { // Lazy Load if (this._Product == null) this._Product = Product.LoadById(this.ProductID); return this._Product; } }
  • 66. // Identity Map public static IDictionary loaded = new Hashtable(); public static Product LoadById(int productID) { // check registry Identity Map Product p = (Product)loaded[productID]; if (p != null) return p; // load Product aux = new Product(); DataSet ds = ExecuteQuery(quot;SELECT * FROM TProducts WHERE productID=quot; + productID); p = new Product(ds.Tables[0].Rows[0]); // save in registry loaded[productID] = p; return p; }
  • 67. public void Save() { BeginTransaction(); object[] parms = new object[] {this.ProductID, this.CustomerID, this.DateSigned, this.Revenue, this.ID}; if (this.ID != 0) { sqlContract = quot;UPDATE TContracts SET productId=?, customerID=?, dateSigned=?, revenue=? WHERE contractID=?quot;; ExecuteTransactedNonQuery(sqlContract, parms); ExecuteTransactedNonQuery(quot;DELETE FROM TRevenueRecognitions WHERE contractID=quot; + this.ID); } else { sqlContract = quot;INSERT INTO TContracts(productId, customerId, dateSigned, revenue) VALUES(?, ?, ?, ?)quot;; this.myID = ExecuteTransactedNonQuery(sqlContract, parms); } foreach (RevenueRecognition r in _RevenueRecognitions) { string sqlRecognition = quot;INSERT INTO TRevenueRecognitions(contractID, dateRecognition, amount) VALUES(?, ?, ?)quot;; object parms[] = new object[] {this.ID, r.DateRecognition, r.Amount}; ExecuteTransactedNonQuery(sqlRecognition, parms); } CommitTransaction(); }
  • 68. Part 5.4
  • 69. UI domain model data mapper BLL DAL Database
  • 70. Customer Contract - _ID : int + property Product : Product - _name : string - _ID : int + property ID : int - _Customer Business logic - _CustomerID : int - _DateSigned : DateTime + Customer ( ) methods only - _ProductID : int + get ID ( ) : int - _Revenue : decimal + property Customer : Customer + property RevenueRecognitions : IList Product + property DateSigned : DateTime + property Type : string + property Revenue : decimal - _ID : int + property ID : int - _name : string ~ AddRecognition ( [in] recognitionDate : DateTime , [in] amount : decimal ) - _type : string + Contract ( ) + property ID : int + RecognizedRevenue ( [in] asOf : DateTime ) : Money + Product ( ) + CalculateRevenueRecognitions ( ) - _Product + get Type ( ) : string + get Product ( ) : Product + get ID ( ) : int ~ SetID ( [in] id : int ) ~ SetProduct ( [in] prodID : int , [in] prodType : string ) - SetStrategy ( [in] prodType : string ) IRevenueRecognitionStrategy ~ SetProduct ( [in] prodID : int ) - theStrategy + get Customer ( ) : Customer + get RevenueRecognitions ( ) : IList RevenueRecognition + get DateSigned ( ) : DateTime + get Revenue ( ) : decimal + Amount : decimal + get ID ( ) : int + DateRecognition : DateTime ~ SetCustomer ( [in] custID : int ) + ID : int + set Product ( [in] value : Product ) + set DateSigned ( [in] value : DateTime ) + RevenueRecognition ( [in] id : int , [in] dt : DateTime , [in] amt : decimal ) 72 + set Revenue ( [in] value : decimal ) + RevenueRecognition ( [in] dt : DateTime , [in] amt : decimal ) ISEP/IP * - _RevenueRecognitions P
  • 71. public void CalculateRevenueRecognitions() { switch (this.Product.Type) { case quot;PTquot;: AddRecognition(this.DateSigned, this.Revenue); break; case quot;FCquot;: decimal[] allocsFC = Money.Allocate(Revenue, 3); AddRecognition(DateSigned, allocsFC[0]); AddRecognition(DateSigned.AddDays(60), allocsFC[1]); AddRecognition(DateSigned.AddDays(90), allocsFC[2]); break; case quot;BDquot;: decimal[] allocsBD = Money.Allocate(Revenue, 3); AddRecognition(DateSigned, allocsBD[0]); AddRecognition(DateSigned.AddDays(30), allocsBD[1]); AddRecognition(DateSigned.AddDays(60), allocsBD[2]); break; } }
  • 72. Pattern GoF Problem: Allow the client the choice of many alternatives, but each is complex, and you don't want to include code for all. Solution: Make many implementations of the same interface, and allow the client to select one and give it back to you.
  • 73. Pattern GoF Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. fonte: Design Patterns: Elements of Reusable Object-Oriented Software
  • 74. public interface IRevenueRecognitionStrategy { void CalculateRevenueRecognitions(); }
  • 75. BaseStrategy + theContract Contract + BaseStrategy ( [in] c : Contract ) + CreateStrategy ( [in] prodType : string , [in] c : Contract ) : IRevenueRecognitionStrategy BDStrategy FCStrategy PTStrategy + BDStrategy ( [in] c : Contract ) + FCStrategy ( [in] c : Contract ) + PTStrategy ( [in] c : Contract ) + CalculateRevenueRecognitions ( ) + CalculateRevenueRecognitions ( ) + CalculateRevenueRecognitions ( ) IRevenueRecognitionStrategy
  • 76. Pattern A layer of Mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself fonte: Patterns of Enterprise Application Architecture
  • 77. IDBHelper DBHelper ContractMapper CustomerMapper ProductMapper + ContractMapper ( ) + CustomerMapper ( ) + ProductMapper ( ) # ApplyMap ( [in] row : DataRow ) : Contract # ApplyMap ( [in] row : DataRow ) : Customer # ApplyMap ( [in] row : DataRow ) : Product # ApplyMap ( [in] dsContractAndRecognitions : DataSet ) : Contract + LoadById ( [in] customerID : int ) : Customer + LoadById ( [in] productID : int ) : Product + LoadById ( [in] contractID : int ) : Contract + LoadAll ( ) : IList + LoadAll ( ) : IList + LoadByProduct ( [in] productID : int ) : IList + LoadByCustomer ( [in] customerID : int ) : IList + LoadAll ( ) : IList + Save ( [in] c : Contract ) IdentityMap # property Registry : IDictionary + IdentityMap ( ) DBFactory # CheckRegistry ( [in] id : int ) : object # AddToRegistry ( [in] id : int , [in] o : object ) + DBFactory ( ) # get Registry ( ) : IDictionary + CreateDBHelper ( ) : IDBHelper 79
  • 78. DBHelper + property CurrentTransaction : IDbTransaction - CONNSTR : string = @quot;Provider=...quot; + DBHelper ( ) + BeginTransaction ( ) + CommitTransaction ( ) + ExecuteNonQuery ( [in] tx : IDbTransaction , [in] cmd : IDbCommand ) : int + ExecuteNonQuery ( [in] sql : string ) : int + ExecuteQuery ( [in] sql : string ) : DataSet IDBHelper + ExecuteTransactedNonQuery ( [in] sql : string ) : int + ExecuteTransactedQuery ( [in] sql : string ) : DataSet + GetConnection ( [in] open : bool ) : IDbConnection + RoolbackTransaction ( ) + get CurrentTransaction ( ) : IDbTransaction + ExecuteTransactedNonQuery ( [in] cmd : IDbCommand ) : int + CreateCommand ( ) : IDbCommand + CreateCommand ( [in] inTransaction : bool ) : IDbCommand + CreateParameter ( [in] name : string , [in] value : object ) : IDataParameter + CreateParameter ( [in] name : string , [in] tp : DbType , [in] value : object ) : IDataParameter + ExecuteQuery ( [in] cmd : IDbCommand ) : DataSet
  • 79. : Actor1 / UI : ContractMapper : DBFactory : BaseStrategy 1 : Form_Load 2 : LoadAll ( ) 3 : CreateDBHelper ( ) 4 : new : IDBHelper 5 : ExecuteQuery ( cmd ) 6 : ApplyMap ( dsContractAnd Recognitions ) 7 : [currentrow.Id != current Contract.ID] new : Contract 8 : SetID ( id ) 9 : SetProduct ( prodID , prodType ) 10 : SetStrategy ( prodType ) 11 : CreateStrategy ( prodType , c ) 12 : AddRecognition ( recognitionDate , amount ) foreach returned recognition 81 ISEP/IP P
  • 80. protected Contract ApplyMap(DataSet dsCNR) { if (dsCNR].Rows.Count < 1) return null; Contract c = new Contract(); c.DateSigned = (DateTime)dsCNR.Tables[0].Rows[0][quot;Datesignedquot;]; c.Revenue = (decimal)dsCNR.Tables[0].Rows[0][quot;Revenuequot;]; c.SetID( (int)dsCNR.Tables[0].Rows[0][quot;IDquot;] ); c.SetProduct( (int) dsCNR.Tables[0].Rows[0][quot;ProductIDquot;], (string) dsCNR.Tables[0].Rows[0][quot;ProdTypequot;] ); c.SetCustomer( (int)dsCNR.Tables[0].Rows[0][quot;CustomerIDquot;] ); foreach (DataRow r in dsCNR.Tables[0].Rows) { c.AddRecognition( (DateTime)r[quot;dateRecognitionquot;], (decimal)r[quot;amountquot;]); } return c; }
  • 81. public class Contract { // !!! to be used only by Mapper internal void SetProduct(int prodID, string prodType) { _ProductID = prodID; SetStrategy(prodType); } private void SetStrategy(string prodType) { theStrategy = BaseStrategy.CreateStrategy(prodType, this); } } 83
  • 82. public abstract class BaseStrategy { protected Contract theContract; public BaseStrategy(Contract c) { theContract = c; } public static IRevenueRecognitionStrategy CreateStrategy( string prodType, Contract c) { switch (prodType) { case quot;PTquot;: return new Strategies.PTStrategy(c); break; case quot;FCquot;: return new Strategies.FCStrategy(c); break; case quot;BDquot;: return new Strategies.BDStrategy(c); break; default: throw new ApplicationException(quot;invalid typequot;); } return null; } }
  • 83. : Actor1 / UI / c : Contract : IRevenueRecognitionStrategy : ContractMapper : DBFactory 1 : btnCalc_Click 2 : CalculateRevenueRecognitions ( ) 3 : CalculateRevenueRecognitions ( ) 4 : AddRecognition ( recognitionDate , amount ) foreach recognized revenue 5 : Save ( c ) 6 : CreateDBHelper ( ) 7 : new : IDBHelper 8 : BeginTransaction ( ) 9 : ExecuteTransactedNonQuery ( cmd ) 10 : ExecuteTransactedNonQuery ( foreach recognized revenue cmd ) 11 : CommitTransaction ( ) 85 ISEP/IP P
  • 84. public void CalculateRevenueRecognitions() { _RevenueRecognitions.Clear(); theStrategy.CalculateRevenueRecognitions(); } // !!! to be used by strategies internal void AddRecognition(DateTime recognitionDate, decimal amount) { _RevenueRecognitions.Add( new RevenueRecognition(recognitionDate, amount) ); }
  • 85. public class BDStrategy : BaseStrategy { public void CalculateRevenueRecognitions() { decimal[] allocs = Money.Allocate(theContract.Revenue, 3); theContract.AddRecognition(theContract.DateSigned, allocs[0]); theContract.AddRecognition(theContract.DateSigned.AddDays(30), allocs[1]); theContract.AddRecognition(theContract.DateSigned.AddDays(60), allocs[2]); } } public class FCStrategy : BaseStrategy { public void CalculateRevenueRecognitions() { decimal[] allocs = Money.Allocate(theContract.Revenue, 3); theContract.AddRecognition(theContract.DateSigned, allocs[0]); theContract.AddRecognition(theContract.DateSigned.AddDays(60), allocs[1]); theContract.AddRecognition(theContract.DateSigned.AddDays(90), allocs[2]); } } public class PTStrategy : BaseStrategy { public void CalculateRevenueRecognitions() { theContract.AddRecognition(theContract.DateSigned, theContract.Revenue); } }
  • 86. Part 6
  • 87. Part 6.1
  • 88. Pattern Prevents conflicts between concurrent business transactions by detecting a conflict and rolling back the transaction 90
  • 89. Pattern fonte: Patterns of Enterprise Application Architecture
  • 90. Pattern Prevents conflicts between concurrent business transactions by allowing only one business transaction at a time to access data 92
  • 91. Pattern fonte: Patterns of Enterprise Application Architecture
  • 92. Part 6.2
  • 93. Pattern GoF Problem: You need a set of related classes but there are several sets with different implementations Solution: Create a service interface, create several implementations of those services, create a factory class for each set of implementations, provide the client with the correct implementation behind the interface
  • 94. Pattern GoF Provides an interface for creating families of related or dependent objects without specifying their concrete classes.
  • 95. Pattern GoF fonte: Design Patterns: Elements of Reusable Object-Oriented Software
  • 96. fonte: Microsoft .Net Pet Shop 3.x
  • 97. Part 6.3
  • 98. public class PersonDataAccess { public PersonDataAccess() { ... } public bool Insert(object r) { ... } public bool Delete(object r) { ... } public bool Update(object r) { ... } public object Load(object id) { ... } } Add logging to this existing class
  • 99. public class PersonDataAccess { public PersonWithLogDataAccess() { ... } public bool Insert(object r) { ... Log.Write(...); } public bool Delete(object r) { ... } public bool Update(object r) What if instead of { ... } logging you public object Load(object id) needed billing? { ... } }
  • 100. public class PersonWithLogDataAccess : PersonDataAcess { public PersonWithLogDataAccess() { ... } public bool Insert(object r) { base.Insert(r); logFile.Write(...); } public bool Delete(object r) { ... } public bool Update(object r) What if you { ... } needed logging public object Load(object id) and billing? { ... } }
  • 101. Pattern GoF Problem: Allow functionally to be layered around an abstraction, but still dynamically changeable. Solution: Combine inheritance and composition. By making an object that both subclasses from anther class and holds an instance of the class, can add new behavior while referring all other behavior to the original class.
  • 102. Pattern GoF Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. fonte: Design Patterns: Elements of Reusable Object-Oriented Software
  • 103. public interface IDataAccess { public bool Insert(object r); public bool Delete(object r); public bool Update(object r); public object Load(object id); }
  • 104. public class PersonDataAccess : IDataAccess { public PersonDataAccess () { ... } public bool Insert(object r) { ... } public bool Delete(object r) { ... } public bool Update(object r) { ... } public object Load(object id) { ... } }
  • 105. public class LoggingDecorator : IDataAccess { IDataAccess component; public LoggingDecorator(IDataAccess component) { this.component = component; } public bool Insert(object r) { WriteLog(quot;Insertquot;, r); return component.Insert(r); } public bool Delete(object r) { ... } public bool Update(object r) { ... } public object Load(object id) { ... } private void WriteLog(string op, object parms) { ... } }
  • 106. public class TesteDecorator { public void Teste() { IDataAccess da = new PersonIDataAccess (); IDataAccess dec = new LoggingDecorator(da); ... dec.Insert(...); ... } }
  • 107. Since the Decorator class implements the same interface of the Component, it can be used anywhere you would use a Component Inheritance wouldnt allow scenarios with only Logging or only billing or both But you can chain Decorators!
  • 108. public class CounterDecorator : IDataAccess { int nAcessos = 0; IDataAccess component; public CounterDecorator(IDataAccess component) { this.component = component; } public bool Insert(object r) { nAcessos++; return component.Insert(r); } public bool Delete(object r) { ... } public bool Update(object r) { ... } public object Load(object id) { ... } public int NumOfAccesses { get { return nAcessos; } } }
  • 109. public class BillingDAL { public void Teste() { IDataAccess da = new PersonDataAccess(); IDataAccess dec = new LoggingDecorator(da); IDataAccess cd = new CounterDecorator(dec); ... cd.Insert(...); ... CounterDecorator bil = (CounterDecorator)cd; float custo = bil.NumOfAccesses * PRICE_PER_OP; ... } }
  • 110. Part 7
  • 111. ... Pattern ... ... Java/C#... Adapter ... Domain model ... ???? ... ... Java/C#... ??? ????
  • 112. ... Pattern ... ... Java/C#... Adapter ... Domain model ... ... Pattern ... ... Java/C#... Adapter ... Domain model ...
  • 113. Architectural style output Business operations http://w2ks.dei.isep.ipp.pt/psousa/GetFile.aspx?file=PoEAAWorkbench.zip
  • 114. public interface IRevenueRecognition { Money RecognizedRevenue(int contractID, DateTime asOf); void CalculateRevenueRecognitions(int contractID); object GetContracts(); object GetCustomers(); object GetProducts(); }
  • 115. common RevenueGUI RevenueFacade Implementations
  • 116. A combination of a Factory, a Singleton and a Faade The desired implementation is loaded into memory and an instance of the specific faade is created (reflection) and returned to the GUI The UI layer will then interact with the business layer via the IRevenueRecognition business interface ignoring the actual implementation
  • 117. Each implementation shows a combination of patterns that form an architectural style Table module + table data gateway Domain model / active record These are the Domain model + data mapper classes the students must look Define the business classes and data access classes necessary to support the application requirements defined by the business interface
  • 118. Transaction Script using DataSet Transaction Script Transaction Script + Data Gateway Transaction Script using Custom Classes Transaction Script Transaction Script + Row Data Gateway Table Module Table Module + Table Data Gateway (DataSet) Table Module + Table Data Gateway (Custom Classes) Domain Model Domain Model + Active Record Domain Model + Data Mapper
  • 119. Part 8
  • 120. contract
  • 121. Pattern An object that encapsulate the code that implements the consumer portion of a contract. They act as proxies to other services, encapsulating the details of connecting to the source and performing any necessary translation. fonte: Enterprise Solution Patterns Using .NET
  • 122. Pattern Hides the details of accessing the service (ex., network protocol) May be considered a data access component Native support from most tools (e.g., Visual Studio, Netbeans, Rational software Architect) by web service proxies
  • 123. Pattern Provides a coarse-grained faade on fine- grained objects to improve efficiency over a network fonte: Patterns of Enterprise Application Architecture
  • 124. Pattern Domain object interfaces are tipically fine grained Inadequeate for remote operations Create a surronding layer above domain objects Local clients use the local interface The facade may encapsulate the interface of one or more business objects Domain objects: Address.New Address.Set Person.AddAddress Person.Update Remote Facade: AddressFacade.AddNewAddressToPerson
  • 125. Pattern An object that carries data between processes in order to reduce the number of method calls. fonte: Patterns of Enterprise Application Architecture
  • 126. Pattern Since XML is the de facto standard DTO should support serialization to/from XML Should be independent of the underlying domain object Should be implemented in accordance with the requiremnts of the remote application CompleteCustomerInfoDTO BasicCustomerInfoDTO Should be independent of the underlying platform (e.g., programming language) DataSet/DataTable .net ResultSet JDBC DateTime .net
  • 127. Pattern Defines an application's boundary with a layer of services that establishes a set of available operations and coordinates the application's response in each operation fonte: Patterns of Enterprise Application Architecture
  • 128. Pattern Domain logic pattern in the context of service orientation May be implemented as a Remote Facade or may be called by a Remote Facade
  • 129. Pattern Hides the complexity of finding and creating service gateways fonte: Core J2EE Patterns
  • 130. Part 9
  • 131. How to represent the business entities? How to persist its state? How to code the Business logic? How to guarantee data coherence? How to handle application distribution?
  • 132. Simple to complex logic and good platform support for Result Sets Table Module Complex logic Domain Model 134
  • 133. Table Module Table Data Gateway Domain Model very similar to DB schema Active Record Complex Domain Model Data Mapper
  • 134. Guarantee that in-memory data is only updated in one place Identity Map Mantain a relation between in-memory objects and database records Identity field Avoid loading the entire DB to memory Lazy Load 136
  • 135. Hibernate e nHibernate www.hibernate.org LINQ http://msdn.microsoft.com/en-us/netframework/aa904594.aspx JDO http://java.sun.com/products/jdo/ SDO http://www.osoa.org/display/Main/Service+Data+Objects+Home EJB 3 http://java.sun.com/products/ejb/ Apache Cayenne http://cayenne.apache.org/ FastObjects j2 http://www.versant.net/eu_en/products/fastobjects_j2/ FastObjects.NET http://www.versant.net/eu_en/products/fastobjects_net/ Prevayler http://www.prevayler.org/wiki.jsp
  • 136. Only in data access layer Dont forget Views Balance dynamic SQL and stored procedures Flexibility Security Easyest implementation for some business logic (e.g., group operations) 138
  • 137. Use parameters instead of string concatenation DELETE is uncommon INSERT causes no locking problem Must be careful on UPDATE Concurrent access Incremental update E.g. Update quantity on hand UPDATE Products SET QtyOnHand = 10 UPDATE Products SET QtyOnHand = QtyOnHand + 2
  • 138.