Upload
vivek-singh
View
130
Download
0
Embed Size (px)
Citation preview
Service Architecture Patterns
Avinash ChughVivek Singh
Rohith Rajagopal
Designs are neither good nor bad. They are more or less useful.
Martin Fowler
Service = SOA service
External InterfaceInterface contractsMaps service contracts to/from domain entitiesInteracts with DDD servicesCross functional responsibility
SOA Services
Services
Mappers
ServiceContract
PersistenceMappings
Repositories
get client requests as
request/response withdomain objects
Factories Entities
Value ObjectsAggregates
DomainModel
find domain objects
orchestrates business logic
read/writedata source
send request contract to
find/save domain objects
Service = DDD Service
Core business logicOrchestrates domain layerReusable within a SOA service
Simple Banking Application
Customer(1)----(*)Account(1)----(*)TransactionOpening an accountView transactions across accountsAccount SummaryCustomer ProfilePerform transaction
Service Responsibility Partition
Breaking system into different services
Minimal Distributed System Architecture
Services
Database
User Interface
Application
Grows to monolithic services
Single databaseEverything gets deployed every timeTest everything every time
Need for smaller sub-systems
Database
Domain Model
Service A
Database
Domain Model
Service B
Database
Domain Model
Service C
Database
Domain Model
Service D
Data Ownership Driven Services
Advantages
Development decouplingIndependent evolutionDeployment independenceLow testing cost
Disadvantages
Inner loop problem/N+1 problem
Database
Domain Model
Account Service
Database
Domain Model
Transaction Service
Customer
Acc 1
Acc2
Acc4
Acc3
Txn1
Txn2
Txn3
Txn1
Txn5
Txn1
Txn2
Txn1
AccountService calling TxnService
GetTransactions(acc1)GetTransactions(acc2)GetTransactions(acc3)GetTransactions(acc4)
Batch Request Interface
GetTransactions(acc1, acc2, acc3, acc4)
Use correlation ids instead of orderDoesn’t solve all problems
Transaction Atomicity
Database
Domain Model
Service A
Database
Domain Model
Service CCustomer
New Acc
Txn1
Transaction
No transaction between service operationsDistributed transaction has performance issues
Database
Domain Model
Service A
Domain Model
Service C
Domain Model
Service B
Domain Model
Service D
Shared Data Services
Database Database
Advantages
Easier resolution of performance issuesTransaction supportDevelopment still quite independent
Disadvantages
Unclear domain boundariesFuzzy ownership of dataCode duplication across services
Tradeoffs
Code duplicationModel duplicationTransactions
Service IndependencePerformanceData Ownershipvs
Service Contract Design
By design & not by side-effect
Service is a productContract is user interface for serviceCan help in multi-version capability
Banking application contract
class OpenAccountRequest { int CustomerId; DateTime StartingDate; AccountType AccountType; Name AccountHolderName; }
class Name { string FirstName; string LastName; }
enum AccountType { Savings, Current }
class OpenAccountRequest { string CustomerId; string StartingDate; string AccountType; Name AccountHolderName; }
class Name { string FirstName; string LastName; }
class OpenAccountRequest { int CustomerId; DateTime StartingDate; AccountType AccountType; Name AccountHolderName; }
class Name { string FirstName; string LastName; }
enum AccountType { Savings, Current }
Possible starting dates
03/11/201003/11/10
03-11-201003-11-10
03-Nov-20103-Nov-10
Possible customer id
008675100004664100004664
8675100004664CC008675100004664
Service
Serve as much as possible
class OpenAccountRequest { string CustomerId; string StartingDate; string AccountType; string AccountHolderFirstName; string AccountHolderLastName; }
class OpenAccountRequest { string CustomerId; string StartingDate; string AccountType; Name AccountHolderName; }
class Name { string FirstName; string LastName; }
Business doesn’t understand objects
….and definitely not class inheritance
class OpenJointAccountRequest : OpenAccountRequest { int AccountNumber; } class OpenAccountRequest { string CustomerId; string StartingDate; string AccountType; string AccountHolderFirstName; string AccountHolderLastName; }
class OpenAccountRequest { string CustomerId; string StartingDate; string AccountType; string AccountHolderFirstName; string AccountHolderLastName; string JointAccountNumber; }
Use flat structure, no nested objectsNested objects only when using list
Business Readable Contract
Service operations are not methods public interface PaymentGatewayService { bool Authorize(string cardNumber, string verifiedByVisaPassword); bool Validate(string cardNumber, string holderName, string cvv); bool PerformTransaction(string cardNumber, string holderName, string cvv, decimal amount); }
interface PaymentGatewayService { CreditCardPaymentResponse Process(CreditCardPaymentRequest request); }
class CreditCardPaymentRequest { string CardNumber; string CVVNumber; string VBVPassword; string HolderName; string Amount; }
class CreditCardPaymentResponse { string ErrorCode; string ErrorDescription; string TransactionReferenceNumber; }
No parameters/return-value/exception
Late Binding over Fail fast
Service side validation over schema validationLoose types over strong typesError message over system exceptions
Also useful in versioning
Decouple Domain Entities and Service Contract
Web Services
Database
User Interface
Application
Data Transfer Objects
UI Model
Domain Model
Extremely Common Architecture
ORMapping objects
Writing different data definition and mapping them
Can be quite tedious and error prone
What can we do?
Web Services
Database
User Interface
Application
Data Transfer Objects
UI Model
Domain Model
ORMapping objects
Hibernate
How about?
Web Services
Database
User Interface
Application
Domain Model
Domain Model
Domain Model
Hibernate
Data Transfer Objects
UI Model
Shared Domain Model
public class Customer { int id; Name name; int age; IList<Account> accounts; } public class Account { int id; string number; DateTime openingDate; DateTime closingDate; decimal balance; IList<Transaction> transactions; } public class Transaction { int id; DateTime date; decimal amount; TransactionType type; }
public enum TransactionType { Credit, Debit } public class Name { string first; string middle; string last; string title; }
Domain Model
public class Customer { int id; string firstName; string middleName; string lastName; string title; int age; }
public class Customer { int id; Name name; int age; }
public class Name { string first; string middle; string last; string title; }
Domain ModelContract
Can tolerate this
public class Customer { string id; string firstName; string middleName; string lastName; string title; string age; }
public class Customer { int id; Name name; int age; }
public class Name { string first; string middle; string last; string title; }
Contract
…even this
Domain Model
public class Customer { int id; int customerId; string firstName; string middleName; string lastName; string title; int age; }
public class Customer { int id; int customerId; string firstName; string middleName; string lastName; string title; int age; }
Contract
Encapsulate service internals
Domain Model
public class Customer { public string CustomerId { get; set; } public string FirstName { get; set; } public string MiddleName { get; set; } public string LastName { get; set; } public string Title { get; set; } public string Age { get; set; } }
public class Customer { int customerId; string firstName; string middleName; string lastName; string title; int age; }
Encapsulate Domain Model
Contract
Domain Model
public class Customer { public string CustomerId { get; set; } public string FirstName { get; set; } public string MiddleName { get; set; } public string LastName { get; set; } public string Title { get; set; } public string Age { get; set; } }
public class Customer { int id; int customerId; Name name; int age; }
public class Name { string first; string middle; string last; string title; }
Too much mismatch
Contract DomainModel
Bad contract and domain
public class CustomerService { private readonly CustomerRepository customerRepository;
public CustomerService(CustomerRepository customerRepository) { this.customerRepository = customerRepository; }
public Customer Get (CustomerServiceRequest request) { try { return customerRepository.LoadCustomer(request. Identification); } finally { customerRepository.Dispose(); } } }
Simple Service Implementation
public class Customer { Name name; int age; }
public class Customer { Name name; IList<Account> accounts; } public class Account { decimal balance; IList<Transaction> transactions; } public class Transaction { int id; DateTime date; decimal amount; TransactionType type; }
public class Customer { Name name; IList<Account> accounts; } public class Account { string number; decimal balance; }
Customer Profile
Accounts Summary
Transactions Since
No control over data sent
<?xml version="1.0" encoding="utf-8" ?><hibernate-mapping > <class name="Customer" table="Customers" lazy="true"> <id name="Id"> <column name="Id" not-null="true" /> <generator class="identity" /> </id> <bag name="accounts" lazy="true" cascade="all-delete-orphan" access="field"> <key column="CustomerId" /> <one-to-many class="Account"/> </bag> </class>
<class name="Account" table="Accounts" lazy="true"> <id name="Id"> <column name="Id" not-null="true" /> <generator class="identity" /> </id> </class></hibernate-mapping>
Serializing lazy domain object
Create Session Load
Customer
Return Customer
Close Session
Get Customer
Customer Response
Lazy entities not loaded
Account 2
IdBalance
Account 1
IdBalance
Customer
IdNameAge
hacks…<?xml version="1.0" encoding="utf-8" ?><hibernate-mapping > <class name="Customer" table="Customers" lazy=“false"> <id name="Id"> <column name="Id" not-null="true" /> <generator class="identity" /> </id> <bag name="accounts" lazy=“false" cascade="all-delete-orphan" access="field"> <key column="CustomerId" /> <one-to-many class="Account"/> </bag> </class>
<class name="Account" table="Accounts" lazy=“false"> <id name="Id"> <column name="Id" not-null="true" /> <generator class="identity" /> </id> </class></hibernate-mapping>
isn’t this smart public Customer Get(CustomerIdentification customerIdentification) { try { Customer customer = customerRepository.LoadCustomer(customerIdentification); ForceLoad(customer); return customer; } finally { customerRepository.Dispose(); } }
private void ForceLoad(Customer customer) { customer.ToString(); customer.Accounts.ForEach(account => account.ToString()); //.....so on }
no…may be should have used reflection
Tradeoffs
Code reuseLess codeMapping simplicity
Design compromiseDecouplingControl serialized dataPerformance
vs
Thank you!