47
ENTERPRISE APPLICATION ARCHITECTURE Data Source Patterns

Enterprise application architecture

  • Upload
    lahela

  • View
    44

  • Download
    0

Embed Size (px)

DESCRIPTION

Data Source Patterns. Enterprise application architecture. Data Source Patterns. Table Data Gateway Row Data Gateway Active Record Data Mapper Unit of Work Identity Map. Table Data Gateway. - PowerPoint PPT Presentation

Citation preview

Page 1: Enterprise application architecture

ENTERPRISE APPLICATION ARCHITECTURE

Data Source Patterns

Page 2: Enterprise application architecture

Data Source Patterns

Table Data Gateway Row Data Gateway Active Record Data Mapper Unit of Work Identity Map

Page 3: Enterprise application architecture

Table Data Gateway

An object that acts as a Gateway to a database table. One instance handles all the rows in the table. Separate SQL from business logic Hide SQL in Gateway from business logic A Table Data Gateway holds all the SQL

for accessing a table. All CRUD operations on the table

Usually a table data gateway is stateless

Page 4: Enterprise application architecture

TDG: Architecture Table Module

businessTransaction1()businessTransaction2()businessTransaction3()

Table Data Gateway

findDataForBusinessTransaction1(sql_query)insertRecordsForBusinessTransaction1(sql_insert, items)updateRecordsForBusinessTransaction2(sql_update, items)…

sql_query= “ SELECT * FROM table 1…”sql_insert = “INSERT into tablei…”sql_update = “ UPDATE tablei …”sql_delete = “DELETE FROM …”

DB

Page 5: Enterprise application architecture

TDG: When to use it

Works best with Table Module Works lest with Domain Model

Page 6: Enterprise application architecture

TDG: Example Database Schema

ProductNametype

Contractdate _signedrevenue

RevenueRecognitionAmount

date

11 * *

Page 7: Enterprise application architecture

TDG: ExampleRecognitionTableModule

calcRecognitions(contract#)recognizedRevenue(contract#, date)…

RecognitationGateway

findRecognitionsFor(contract#, date)insertRecognition(contract#, revenue, date)…

Sql_findContract = “ SELECT * FROM Contract WHERE id = ?”Sql_findRecogns = “select* from recog Where cid=? and date<?”……

DB

Page 8: Enterprise application architecture

createContract()

Sequence Diagram: test cases

:RecognitionTableModule :RecognitionGateway

:Tester

insertContract()INSERT into contract …

:Database

calcRecognitions()insertRecognition()

INSERT iinto Recog…

recognizedRevenue()findRecognitionsFor()

SELECT * FROM …

insertRecognition()INSERT iinto Recog…

INSERT iinto Recog…insertRecognition()

:ContractTableModule :ContractGateway

Page 9: Enterprise application architecture

TDG: Example

class RecognitionGateway { static String findRecogns = “SELECT * FROM revenueRecognition WHERE contract = ? And date <= ?”; static String findContract = “SELECT * FROM contract c, product p WHERE c.id = ? And c.pid = p.id”; public ResultSet findRecognitionsFor(int contrno, Date d) { PreparedStatement s = db.prepareStatement(findRecongs); s.setInt(1, contrno); s.setDate(2, d); ResultSet result = s.executeQuery(); return result; }}Class ContractGateway { public ResultSet findContract(int contrno) { PreparedStatement s = db.prepareStatement(findContract); s.setInt(1, contrno); ResultSet result = s.executeQuery(); return result; }}

Page 10: Enterprise application architecture

TDG: Exampleclass RecognitionTableModule { private RecognitionGateway gw = new RecognitionGateway(); public Money recognizedRevenue(int contrno, Date d) { Money Result = Money.dollar(0); ResultSet rs = gw.findRecognitionsFor(contrno, d); while (rs.next()) { result = result.add(rs.getBigDecimal(“amount”)); } return result; } public void calculateRevenueRecognitions(int contrno) { ResultSet contrs = contractGateway.findContract(contrno); totalRevenue = contrs.getBigDecimal(“revenue”); dates = contrs.getDate(“date_signed”); type = contrs.getChar(“type”); if (type == ‘S’) { gw.insertRecognition(contrno, totalRevenue/3, date); gw.insertRecognition(contrno, totalRevenue/3, date+60); gw.insertRecognition(contrno, totalRevenue/3, date+90); } else if (type = ‘W’) { gw.insertRecognition(contrno, totalRevenue, date); } else if (type == ‘D’ { ... }...

Page 11: Enterprise application architecture

Row Data Gateway

An object that acts as a gateway to a single record in a table.

Page 12: Enterprise application architecture

RDG: Example - PersonGateway

PersonID int primary key,last varchar(30),First varchar(30),Dependents int

PersonGatewaylast, first, numOfDepsinsert()update()delete()

DB

PersonFinderlast, first, numOfDepsfind(int ID)findParents()

RegistrygetPerson(ID) addPerson(person)

Page 13: Enterprise application architecture

RDG: Example – Person Recordclass PersonGateway { int id; String last; String first; int numOfDeps;

// getters and setters Static String updateSQL = “UPDATE person SET last = ?, first =?, dependents = ? WHERE id =?”; static String insertSQL = “...”; static String delete = “... “;

void update() { PreparedStatement st= null; try { st = db.prepareStatment(updateSQL); st.setString(1, last); st.setString(2, first); st.setInt(3, numOfDeps); st.setInt(4, id); st.execute(); } catch (SQLException) { ... } } void insert() { ... } void delete() { ... }}

Page 14: Enterprise application architecture

RDG: Example – PersonFinderclass PersonFinder { Static String personSQL = “SELECT * FROM person WHERE id =?”; static String parentsSQL = “SELECT * FROM person WHERE dependents >0”; PersonGateway find(int ID) { PersonGateway result = Registry.getPerson(ID); if (result != null) return result;

PreparedStatement st= null; try { st = db.prepareStatment(personSQL); st.setString(1, ID); ResultSet rs = st.executeQuery(); rs.next(); result = PersonGateway.load(rs); return result; } catch (SQLException) { ... } finally { db.cleanup(st, rs); } }}

Page 15: Enterprise application architecture

RDG: Example – PersonFinderclass PersonFinder { Static String personSQL = “SELECT * FROM person WHERE id =?”; static String parentsSQL = “SELECT * FROM person WHERE dependents >0”; List<PersonGateway> findParents() { List<PersonGateway> result = new ArrayList(); PreparedStatement st= null; try { st = db.prepareStatment(parentSQL); ResultSet rs = st.executeQuery(); while (rs.next()) { result.add(PersonGateway.load(rs)); } return result; } catch (SQLException) { ... } finally { db.cleanup(st, rs); } }}

Page 16: Enterprise application architecture

RDG: PersonGateway.load()class PersonGateway { ... Static PersonGateway load(ResultSet rs) { int id = rs.getInt(1);

PersonGateway result = Registry.getPerson(id); if (result != null) { return result; } result = new Person(id, rs.getString(2), rs.getString(3), rs.getInt(4)); Registry.add(result);

return result; }}

Page 17: Enterprise application architecture

PersonGateway

:PersonFinder

Find(id=123)

RDG: Sequence Diagram: find(id=123)

:TableModule

Registry

getPerson(123)

new

Return ResultSet

return aPerson

DB

SELECT

aPerson:PersonGateway

Return null

load(ResultSet)

newAdd(aPerson)

return aPerson

Page 18: Enterprise application architecture

RDG: As a Data Holder

class Person { private PersonGateway data; public Person(PersonGateway data) { this.data = data; } public int getNumberOfDependents() { return data.getNumberOfDependents(); } public Money getExemption() { Money baseExemption = 1500; Money dependentExemption = 750; return baseExemption + dependentExemption * data.getNumberOfDependents(); }}

When RowDataGateway is used with Domain Model. RowDataGateway may be employed as a data holder of the domain object.

Page 19: Enterprise application architecture

Active Record

An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data Putting DB access login in the domain

object. Each active record is responsible for DB

access as well as domain logic Find methods may be static of

ActiveRecord Find methods may be put into a Finder

class.

Page 20: Enterprise application architecture

AR: ArchitecturePerson(Table_1)Person (ActiveRecord1)

LastNamefirstNamenumberOfDependents

IdlastNamefirstNameNumberOfDependents

Table_2

CRUD operationsgetExemptionisFlaggedforAuditgetTaxableEarnings Attributes

Table_nActiveRecordn

CRUD operations on Table_nBusiness Logic related to Table_n

Attributes

Database

Page 21: Enterprise application architecture

AR: Example – Person Recordclass Person{ int id; String last; String first; int numOfDeps;

// getters and setters Static String updateSQL = “UPDATE person SET last = ?, first =?, dependents = ? WHERE id =?”; static String insertSQL = “...”; static String delete = “... “;

void update() { PreparedStatement st= null; try { st = db.prepareStatment(updateSQL); st.setString(1, last); st.setString(2, first); st.setInt(3, numOfDeps); st.setInt(4, id); st.execute(); } catch (SQLException) { ... } } void insert() { ... } void delete() { ... }}

Page 22: Enterprise application architecture

AR: Person.load()class Person { ... Static Person load(ResultSet rs) { int id = rs.getInt(1);

Person result = Registry.getPerson(id); if (result != null) { return result; } result = new Person(id, rs.getString(2), rs.getString(3), rs.getInt(4)); Registry.add(result);

return result; }}

Page 23: Enterprise application architecture

AR: Domain Logicclass Person { ... public Money getExemption() { Money baseExemption = Money.dollars(1500); Money dependentExemption = Money.dollar(7500); Money totalDepExemptions = dependentExemption.multiply( this.getNumberOfDependent()); Money totalExemption.add(totalDepExemptions);

return totalExemptions; }}

Page 24: Enterprise application architecture

Data Mapper

A layer of Mappers that moves data between objects and a database while keeping them independent of each and the mapper itself. The data mapper layer is to separate the in-

memory objects from the database It allows the object model and database to evolve

(or be designed) independently A mapper is an object that sets up a

communication between two independent objects Works best with Domain Model Use existing framework for data mappers

(Hibernate)

Page 25: Enterprise application architecture

DMP: How It Works

A simple mapper maps a database table to an memory class on a field-to-field basis

A find method of a mapper loads the object from DB. Identify Map may be used to keep one copy in memory For insert and update, the mapping knows what new

objects have been created and what have been destroyed.

A request from client could load a graph of object from DB A purchase order contains multiple line items

Unit of Work determines what to write back to DB Lazy Load may be used to postpone the load of some

objects.

Page 26: Enterprise application architecture

DMP: When to Use It

When the object model and database schema need to evolve independently

When Domain Model is used for domain logic. The object model does not know the

structure of the database Don’t use Data Mapper without

Domain Model

Page 27: Enterprise application architecture

DMP: A Simple Data Mapperclass Person{ String last; String first; int numOfDeps;

...}Class PersonMapper extends AbstractMapper { static Person find(int id) { return (Person) abstractFind(id); } String findStatement() { return “SELECT * FROM person WHERE id =?”; } DomainObject doLoad(int id, ResultSet rs) { String lName = rs.getString(2); String fName = rs.getString(3); int numOfDependents = rs.getInt(4); return new Person(id, lName, fName, numOfDependents); }...}

findStatement() – part of TemplateMethod, to be called by AbstractMapper.abstractFind(); to be called by

AbstractMapper.load();

Page 28: Enterprise application architecture

DMP: A Simple Data MapperClass AbstractMapper { protected Map identityMap = new HashMap(); abstract String findStatement(); DomainObject abstractFind(int id) { DomainObject result = (DomainObject)identityMap(id); if (result != null) return result; PreparedStatement findStmt; try { findStmtn = DB.prepareStatement(findStatement()); findStmt.setInt(1, id); ResultSet rs = findStmt.executQuery(); rs.next(); result = load(rs); return result; } catch (SQLException) { ... } finally { DB.cleanup(); } }

Template Method Pattern here: findStatement() to be implemented by PersonMapper

Page 29: Enterprise application architecture

DMP: A Simple Data MapperClass AbstractMapper { DomainObject load(ResultSet rs) { int id = rs.getInt(1); if (identityMap.containsKey(id) return (DomainObject) identityMap.get(id); DomainObject result = doLoad(id, rs); identityMap.put(id, result); return result; }}

Defined in PersonMapper.doLoad()

Page 30: Enterprise application architecture

DMP: A Simple Data MapperClass PersonMapper extends AbstractMapper { static String updateSQL = “UPDATE person SET lastName = ? ...”; public void update(Person subject) { PreparedStatement update = null; try { update = DB.prepareStatement(updateSQL); update.setInt(1, subject.getId()); update.setString(2, subject.getLastName()); update.setString(3, subject.getFirstName()); update.setInt(4, subject.getNumberOfDependents()); update.execute(); } catch (SQLException) { ... } finally { DB.cleanup(update); } } public void insert(Person subject) { // similar to update() }}

Page 31: Enterprise application architecture

DMP: Separate Finders

ArtistFinderfind(id)

DomainObjectArtist Id: long

Album

AbstractMapper

ArtistMapper

Mapper Package

Domain Package

Insert()Update()Load()findStatement()doLoad()

find(id)findStatement()doLoad()

Dependency

Injection Principle

Page 32: Enterprise application architecture

DMP: Separate FindersInterface ArtistFinder { Artist find(int id);}Class ArtistMapper extends AbstractMapper implements ArtistFinder { public Artist find(int id) { return (Artist) abstractFind(id); } String findStatement() { return “SELECT * FROM Artist WHERE id = ?”; }}Class AbstractMapper { // similar to the AbstractMapper of previous example}

Page 33: Enterprise application architecture

Unit of Work

Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.. To keep track of what objects have changed

so they can be written back to the DB Batch multiple DB statements for

performance Implement transactions To control concurrency related problems

Page 34: Enterprise application architecture

UoW: Architecture

Unit of WorkregisterNew(object)registerDirty(object)registerClean(object)registerDelete(object)commit()

DB

Page 35: Enterprise application architecture

UoW: How It Works

Each time you touch the database, create a Unit of Work.

Every time you create, change, or delete an object, you tell the Unit of Work.

When you are done, ask the Unit of Work to commit

The Unit of Work opens a transaction, does needed concurrency control, and writes changes to the DB.

Page 36: Enterprise application architecture

UoW: How to Tell Unit of Work Caller Registration: the client of an object

has to register the object with the Unit of Work.

Object Registration: Each object will register itself when it is changed. For example: Load an object: UnitOfWork.registerClean(obj); Setters: UnitOfWork.registerDirty(obj)

Unit of Work Controller: UnitOfWork keeps a copy of each object when loaded from DB, then compares with the actual object when committing.

Page 37: Enterprise application architecture

UoW: ExampleClass UnitOfWork{ private List newObjects = new ArrayList(); private List dirtyObjects = new ArrayList(); private List removedObjects = new ArrayList();

public void registerNew(DomainObject obj) { newObjects.add(obj); } public void registerDirty(DomainObject obj) { dirtyObjects.add(obj); } public void registerRemoved(DomainObject obj) { removedObjects.add(obj); } public void registerClean(DomainObject obj) { if (!identityMap.contains(obj.getId())) { identityMap.add(obj.getId(), obj); } } }

Page 38: Enterprise application architecture

UoW: ExampleClass UnitOfWork{ // commit() public void commit() { insertNew(); updateDirty(); deleteRemoved(); } public void insetNew() { for (Iterator objs = newObjects.iterator(); objs.hasNext()) { DomainObject obj = (DomainObject)objs.next(); MapperRegistry.getMapper(obj.getClass()).insert(obj); } } public void updateDirty() { // similar to insertNew() } public void deleteRemoved() { // similar to insertNew() } }

Page 39: Enterprise application architecture

UoW: Example

// one UnitOfWork per ThreadClass UnitOfWork{ Private static ThreadLocal current = ThreadLocal(); public static void newCurrent() { setCurrent(new UnitOfWord()); } public static void setCurrent(UnitOfWork, uow) { current.set(uow); } public static UnitOfWork getCurrent() { return (UnitOfWork) current.get(); }}

Each business transaction is carried out in a Thread Create a UnitOfWork local of the Thread

Page 40: Enterprise application architecture

UoW: Example// Abstract Class that handles registrationClass DomainObject { protected void markNew() { UnitOfWOrk.getCurrent.registerNew(this); } protected void markClean() { UnitOfWOrk.getCurrent.registerClean(this); } protected void markDirty() { UnitOfWOrk.getCurrent.registerDirty(this); } protected void markRemoved() { UnitOfWOrk.getCurrent.registerRemoved(this); }}

Page 41: Enterprise application architecture

UoW: Example// how a domain object register with Unit of WorkClass Album extends DomainObject { // attributes public static Album create(String name) { Album obj = new Album(IdGenerator.nextId(), name); obj.markNew(); return obj; } pubic void setTitle(String title) { this.title = title; markDirty(); }}// how domain logic employs Unit Of WorkClass EditAlbumTransactionScript { public static void updateTitle(int albumId, String title) { UnitOfWOrk.newCurrent(); Mapper mapper = MapperRegistry.getMapper(Album.class); Album album = (Album)Mapper.find(albumId); album.setTitle(title); UnitOfWork.getCurrent().commit(); }}

Page 42: Enterprise application architecture

Identity Map

Ensure that each object gets loaded only once by keeping every loaded object in a map. We don’t want to have two separate

objects from the same DB record. Looks up objects using the map

when referring to them If a record in memory as an object, don’t

load it from the DB again

Page 43: Enterprise application architecture

IM: How It Works

Each DB table corresponds to an Identity Map

Choice of Keys: the key of the map may be the primary key or a surrogate key

Explicit or Generic: An explicit map is accessed with distinct

methods for each kind of objects, e.g., findPerson(123)

A generic map uses a single method for all kinds of objects, e.g., find(“Person”, 123)

Page 44: Enterprise application architecture

IM: How It Works

How Many: A single map for the session One map per class/table

A single map for each inheritance tree Where to Put Them:

Inside the Unit Of Work if exists, or Inside a Registry of the session

Page 45: Enterprise application architecture

IM: When to Use It

To avoid multiple objects to the same DB record

To act as a cache for DB reads Don’t use it for immutable objects Don’t use it for dependent objects

which are to be handled by their parent.

Page 46: Enterprise application architecture

IM: ExampleClass PersonIdentityMap { private Map persons = new HashMap();

public static void addPerson(Person p) { soleInstance.persons.put(p.getId(), p); }

pubic static Person getPerson(int id) { return (Person) soleInstance.persons.get(id); }}

Page 47: Enterprise application architecture

Data Source: Summary

Table Data Gateway Row Data Gateway Active Record Data Mapper Unit of Work Identity Map