patterns of enterprise application architecture (by example)

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

Upload: paulo-sousa

Post on 06-May-2015

11.142 views

Category:

Business


6 download

TRANSCRIPT

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 thatoccurs 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 thissolution 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 aspectsof a common design structure thatmake it useful for creating a reusableobject-oriented design.Design Patterns-Elements of Reusable Object-orientedSoftware, Gamma et al. (Gang of Four) 6. a set of best practicesa typified solution to a problem in a given contexta way to facilitate communicationfound not invented 7. Patterns are half-bakedMartin Fowler 8. No direct code reusePattern overloadExperience-based validationHard work integrating patterns in the development process8 9. name Contributes to the patternvocabulary synopsis Short description of the problem thepattern will solve. forces Requirements, considerations, ornecessary 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 ArchitecturePoEAA Patterns of Enterprise Application Architecture CJP Core J2EE PatternsESP Enterprise Solution Patterns using Microsoft .NET10 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 theenvironment, and the principlesgoverning its design and evolution.ANSI/IEEE Std 1471-2000, Recommended Practice forArchitectural Description of Software-Intensive Systems 14. fonte: Application Architecture for .NET:designing applications and Services 15. Layersfonte: 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 recognitionThree different productsWord processors, databases, spreadsheetsDifferent payment rulesWP all at once (t)DB 3 payments: t, t+30, t+60SS 3 payments: t, t+60, t+90 (From Martin Fowlers PoEAA book) 20. Customer ContractProduct1 * + name + revenue + name* + dateSigned1 1 WordProcessorSpreadSheet*RevenueRecognition+ dateRecognition DataBase+ amount 21. table TRevenueRecognitions+ Column ID : int + Column contractID : int + Column dateRecognition : date + Column amount : currencytable tableTContractsTProducts 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 behaviourStructure + behaviour?Structure with behaviour?Behaviour (and attributes)?Persistence structure conceptual structure? 25. Custom classesXMLDataSet (.net) / ResultSet (JDBC) 26. Custom classesIList / ListXMLDataSet (.net) / ResultSet (JDBC) 27. User defined code with members for the entitys attributes Product + Type : int + Name : string + ID : int 28. ProductContractCustomer + 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 orientedBLLTable ModuleDALTable Data GatewayObject oriented BLLDomain ModelActive Record DAL BLLData Mapper 35. Part 5.1 36. UIUses theplatforms ResultSet forsharing entity datatable module beteween layersBLL table data gatewayDAL 37. Pattern A single instance that handles the business logic for all rows in a database table or viewfonte: 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 ( ) : DataTableProduct + Product ( )+ GetProducts ( ) : DataTable 39. Pattern An object that acts as a Gateway to a database table. One instance handles all the rows in the tableBusinessentityfonte: Patterns of Enterprise Application Architecture 40. CustomerGateway + CustomerGateway ( )+ GetCustomers ( ) : DataTableContractGateway 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 ) : DataSetBaseGateway # 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 ( ) : OleDbTransactionP 41. / User : Actor1/ GUI/ ClassifierRole1 : Contract/ ClassifierRole2 : Contract Gateway 1 : Form_Load2 : GetContracts ( )3 : GetContracts ( ) 4 : GetConnection ( open )5 : ExecuteQuery ( cnx , sql ) 42. / User : Actor1/ GUI / ClassifierRole1 : Contract 1 : btnCalcRevenues_Click2 : CalculateRevenueRecognitions (contractID ) 3 : new/ ClassifierRole2 : ContractGateway4 : 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 andDAL.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 moduleEntities related behaviour)BLL table data gatewayDAL 47. ProductContractCustomer + 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 ) : IListCustomer+ 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 ( ) : OleDbTransactionP 50. / User : Actor1 / GUI/ ClassifierRole1 : Contract 1 : Form_Load2 : GetContracts ( )/ ClassifierRole2 : Contract3 : newGateway4 : GetContracts ( ) 5 : GetConnection ( open ) 6 : ExecuteQuery ( cnx , sql ) 7 : new / IList 8 : new/ ClassifierRole3 : Contractforeach 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 : Contract7 : new/ dalP : ProductGateway8 : GetProductByID ( productID )9 : CreateProductObject ( r )10 : new/ p : Product11 : 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 recordBLL + 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 datafonte: Patterns of Enterprise Application Architecture 58 57. Can be divided:Classes(a) ActiveRecord BLL/DAL(b) DBHelper ActiveRecord # myID : int # property CurrentTransaction : OleDbTransactionProduct - 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 ) Contract0..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 : OleDbTransactionActiveRecord- CONNSTR : string = @quot;Provider=...quot;# myID : int+ DBHelper ( )+ property ID : int+ GetConnection ( )+ Save ( )+ ExecuteQuery ( )+ ActiveRecord ( )+ ExecuteTransactedQuery ( )+ get ID ( )+ ExecuteNonQuery ( )+ ExecuteTransactedNonQuery ( )+ ExecuteNonQuery ( )+ BeginTransaction ( )+ CommitTransaction ( )+ RoolbackTransaction ( )ProductContractCustomer+ 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_Load2 : LoadAll ( ) 3 : ExecuteQuery ( sql ) 4 : new/ IList 5 : Contract ( row ) / ClassifierRole2 : Contract6 : Add foreachreturned row 60. Networks of objectsE.g. Invoice heading relates to invoice detailsInvoice details refers to ProductsProducts refers to SuppliersWhat 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 Architecture64 63. / User : Actor1/ GUI: ContractSequence 1 : btnCalcRevenues_ClickCalculateRevenues2 : 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 RevenueRecognitionobject in _RevenueRecognitions11 : 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 Loadif (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 MapProduct 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 INTOTRevenueRecognitions(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 mapperBLLDAL Database 70. CustomerContract- _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 *- _RevenueRecognitionsP 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 toinclude code for all.Solution:Make many implementations of the sameinterface, and allow the client to select one andgive 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 ) : IRevenueRecognitionStrategyBDStrategyFCStrategyPTStrategy+ 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 DBHelperContractMapper CustomerMapperProductMapper+ 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: IDBHelper5 : ExecuteQuery ( cmd )6 : ApplyMap ( dsContractAndRecognitions ) 7 : [currentrow.Id != currentContract.ID] new : Contract 8 : SetID ( id ) 9 : SetProduct ( prodID , prodType )10 : SetStrategy ( prodType ) 11 : CreateStrategy ( prodType , c ) 12 : AddRecognition ( recognitionDate, amount )foreach returnedrecognition81 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 Mapperinternal 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 : DBFactory1 : btnCalc_Click 2 : CalculateRevenueRecognitions ( )3 : CalculateRevenueRecognitions ( )4 : AddRecognition ( recognitionDate ,amount )foreach recognized revenue5 : Save ( c ) 6 : CreateDBHelper ( )7 : new: IDBHelper8 : BeginTransaction ( ) 9 : ExecuteTransactedNonQuery ( cmd)10 : ExecuteTransactedNonQuery (foreach recognized revenue cmd ) 11 : CommitTransaction ( )85ISEP/IPP 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 transaction90 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 data92 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 areseveral sets with different implementationsSolution:Create a service interface, create severalimplementations of those services, create afactory class for each set of implementations,provide the client with the correctimplementation 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 anabstraction, but still dynamically changeable.Solution:Combine inheritance and composition. By makingan object that both subclasses from anther classand holds an instance of the class, can add newbehavior while referring all other behavior to theoriginal 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 ComponentInheritance wouldnt allow scenarios with only Logging or only billing or bothBut 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 styleoutputBusinessoperations 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 styleTable module + table data gatewayDomain model / active record These are theDomain model + data mapperclasses the studentsmust lookDefine 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 networkfonte: 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 applicationCompleteCustomerInfoDTOBasicCustomerInfoDTO Should be independent of the underlying platform (e.g., programming language)DataSet/DataTable .netResultSet JDBCDateTime .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 operationfonte: 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 gatewaysfonte: 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 ModuleComplex logic Domain Model 134 133. Table Module Table Data GatewayDomain Model very similar to DB schema Active RecordComplex Domain Model Data Mapper 134. Guarantee that in-memory data is only updated in one place Identity MapMantain a relation between in-memory objects and database records Identity fieldAvoid loading the entire DB to memory Lazy Load136 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 proceduresFlexibilitySecurity 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 UPDATEConcurrent accessIncremental updateE.g. Update quantity on handUPDATE Products SET QtyOnHand = 10UPDATE Products SET QtyOnHand = QtyOnHand + 2 138. Good scalability and user-centered availability Optimistic LockUser cannot loose changes Pessimistic Lock 139. How to pass objects in a remote call? Data Transfer ObjectHow to remotely use objects with fine grained interface? Remote FaadeHow to define a coherent set of operations? Service Layer / Service InterfaceHow to hide the implementation details of the remote call? Service Gateway141 140. Patterns are a good thing Patterns can be difficult Driven by example Will make it easier142 141. Fell free to download the PoEAA Workbench http://w2ks.dei.isep.ipp.pt/psousa/GetFile.aspx?file=PoEAAWorkbench.zip Slides available thru SlideSharehttp://www.slideshare.net/pagsousa143 142. Paulo Sousa [email protected] http://linkedin.com/in/pagsousa 143. Paulo Sousa [email protected] Instituto Superior de Engenharia do Porto 144. Fowler, Martin. Patterns of Enterprise Application Architecture. Adisson-Wesley. Erich Gamma, Richard Helm, Ralph Johnson, John Vissides. Design patterns : elements of reusable object-oriented software. Adisson-Wesley. Frank Buschmann, Regine Meunier, Hans Rohnert, Peter Sommerlad, Michael Stal. Pattern- oriented Software Architecture: System of Patterns. Buschmann, F.; Henney, K. And Schmidt, D. (2007) Pattern-Oriented Software Architecture: A Pattern Language for Distributed Computing, Volume 4. Willey. Deepak Alur, John Crupi and Dan Malks. Core J2EE Patterns: Best Practices and Design Strategies. Prentice Hall / Sun Microsystems Press. http://java.sun.com/blueprints/corej2eepatterns/index.html Enterprise Solution Patterns Using Microsoft .NET. Microsoft Press. http://msdn.microsoft.com/architecture/patterns/default.aspx?pull=/library/en- us/dnpatterns/html/Esp.asp Designing Data Tier Components and Passing Data Through Tiers. Microsoft Patterns & Practices. http://msdn.microsoft.com/library/?url=/library/en- us/dnbda/html/BOAGag.asp?frame=true 147 145. GoF Design patterns (em C#) http://www.dofactory.com/Patterns/Patterns.aspx GoF & POSA Design patterns (em Java) http://www.vico.org/pages/PatronsDisseny.html Patterns of Enterprise Application architecture http://martinfowler.com/eaaCatalog/ Core J2EE Patterns http://www.corej2eepatterns.comEnterprise Solution Patterns Using Microsoft .NET. http://msdn.microsoft.com/architecture/patterns/default.aspx?pull=/library/en- us/dnpatterns/html/Esp.aspPatterns of Enterprise Application Integration http://www.enterpriseintegrationpatterns.com/Enterprise Java Patterns @ The Server Side http://www.theserverside.com/patterns/index.tssMicrosoft Data Patterns http://msdn.microsoft.com/architecture/patterns/default.aspx?pull=/library/en- us/dnpatterns/html/Dp.asp