Download Enterprise application architecture

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project

Transcript
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
 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
TDG: Architecture
Table Module
businessTransaction1()
businessTransaction2()
businessTransaction3()
Table Data Gateway
sql_query= “ SELECT * FROM table 1…”
sql_insert = “INSERT into tablei…”
sql_update = “ UPDATE tablei …”
sql_delete = “DELETE FROM …”
findDataForBusinessTransaction1(sql_query)
insertRecordsForBusinessTransaction1(sql_insert, items)
updateRecordsForBusinessTransaction2(sql_update, items)
…
DB
TDG: When to use it
 Works best with Table Module
 Works lest with Domain Model
TDG: Example
 Database Schema
Product
Name
type
1 *
Contract
date _signed
revenue
1 *
RevenueRecognition
Amount
date
TDG: Example
RecognitionTableModule
calcRecognitions(contract#)
recognizedRevenue(contract#, date)
…
RecognitationGateway
Sql_findContract = “ SELECT * FROM Contract WHERE id = ?”
Sql_findRecogns = “select* from recog Where cid=? and date<?”
……
findRecognitionsFor(contract#, date)
insertRecognition(contract#, revenue, date)
…
DB
Sequence Diagram: test cases
:ContractGateway
:ContractTableModule
:Tester
:RecognitionTableModule
:RecognitionGateway
:Database
createContract()
insertContract()
INSERT into contract …
calcRecognitions()
insertRecognition()
insertRecognition()
insertRecognition()
INSERT iinto Recog…
INSERT iinto Recog…
INSERT iinto Recog…
recognizedRevenue()
findRecognitionsFor()
SELECT * FROM …
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;
}
}
TDG: Example
class 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’ {
...
}
...
Row Data Gateway
 An object that acts as a gateway to a single
record in a table.
RDG: Example - PersonGateway
PersonFinder
PersonGateway
last, first, numOfDeps
last, first, numOfDeps
find(int ID)
findParents()
Registry
getPerson(ID)
addPerson(person)
DB
Person
ID int primary key,
last varchar(30),
First varchar(30),
Dependents int
insert()
update()
delete()
RDG: Example – Person Record
class 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() { ... }
RDG: Example – PersonFinder
class 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); }
}
}
// Registry holds an identity map.
RDG: Example – PersonFinder
class 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); }
}
}
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 PersonGateway(id, rs.getString(2),
rs.getString(3),
rs.getInt(4));
Registry.add(result);
return result;
}
}
RDG: Sequence Diagram: find(id=123)
:TableModule
new
:PersonFinder
Registry
DB
Find(id=123)
getPerson(123)
PersonGateway
Return null
SELECT
Return ResultSet
load(ResultSet)
new
Add(aPerson)
return aPerson
return aPerson
aPerson:PersonGateway
RDG: As a Data Holder
When RowDataGateway is used with Domain Model. RowDataGateway
may be employed as a data holder of the domain object.
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();
}
}
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 logic 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.
AR: Architecture
Person (ActiveRecord1)
LastName
firstName
numberOfDependents
CRUD operations
getExemption
isFlaggedforAudit
getTaxableEarnings
Person(Table_1)
Id
lastName
firstName
NumberOfDependents
Table_2
Attributes
Database
ActiveRecordn
CRUD operations on Table_n
Business Logic related to Table_n
Table_n
Attributes
AR: Example – Person Record
class 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() { ... }
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;
}
}
AR: Domain Logic
class 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;
}
}
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)
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.
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
DMP: A Simple Data Mapper
class Person{
String last;
String first;
int numOfDeps;
findStatement() – part of
TemplateMethod, to be called by
AbstractMapper.abstractFind();
...
to be called by
}
AbstractMapper.load();
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);
}
...
}
DMP: A Simple Data Mapper
Class 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;
Template Method
} catch (SQLException) { ... }
Pattern here:
finally {
findStatement() to be
DB.cleanup();
implemented by
}
PersonMapper
}
DMP: A Simple Data Mapper
Class 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()
DMP: A Simple Data Mapper
Class 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()
}
}
DMP: Separate Finders
Domain Package
DomainObject
Artist
Id: long
Album
ArtistFinder
find(id)
Mapper Package
AbstractMapper
Insert()
Update()
Load()
findStatement()
doLoad()
Dependency
Injection
Principle
ArtistMapper
find(id)
findStatement()
doLoad()
DMP: Separate Finders
Interface 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
}
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
UoW: Architecture
Unit of Work
registerNew(object)
registerDirty(object)
registerClean(object)
registerDelete(object)
commit()
DB
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.
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.
UoW: Example
Class 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);
}
}
}
UoW: Example
Class 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()
}
}
UoW: Example
 Each business transaction is carried out in a Thread
 Create a UnitOfWork local of the Thread
// one UnitOfWork per Thread
Class 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();
}
}
UoW: Example
// Abstract Class that handles registration
Class 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);
}
}
UoW: Example
// how a domain object register with Unit of Work
Class 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 Work
Class 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();
}
}
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
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)
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
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.
IM: Example
Class 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);
}
}
Data Source: Summary
 Table Data Gateway
 Row Data Gateway
 Active Record
 Data Mapper
 Unit of Work
 Identity Map
Similar
PracticeExam1A
PracticeExam1A
Calling Methods
Calling Methods
solutions
solutions