aesthetics and the beauty of an architecture
DESCRIPTION
CQRS & Event Sourcing are patterns gaining traction and popularity. In this presentation given at Øredev 2013 it talks about real-world experiences using these patterns, the good, the bad and the ugly.TRANSCRIPT
Aesthetics & the Beauty of an Architecture
Tom Scott @tomwscott
Adventures in CQRS & Event Sourcing
What’s in it for you?
• Understanding of CQRS & Event Sourcing in the wild
• The mistakes we made…
• … the lessons we learnt …
• … and why we’re still happy!
rob.knight
art 1 |ɑːt| noun 1 [ mass noun ] the expression or application of human creative skill and imagination, typically in a visual form such as painting or sculpture, producing works to be appreciated primarily for their beauty or emotional power
art 1 |ɑːt| noun 1 [ mass noun ] the expression or application of human creative skill and imagination, typically in a visual form such as painting or sculpture, producing works to be appreciated primarily for their beauty or emotional power
Aesthetics
Scott’s Purely Arbitrary Criteria, Etceteras
For Evaluating Beautiful Architectures
Etceteras
Aesthetics
#SPACE_FEBA
Fully Operational
Simplicityecherries
Commitmentjustageek
Deferring Commitmentjustageek
MetaphorPetar Pavlov - http://bit.ly/9ySEUt
Discipline & Consistency
Serendipity
kathryn_rotondo
• Fully Operational
• Simplicity
• Deferred Commitment
• Metaphor
• Discipline
• Serendipity
#SPACE_FEBA
Price Comparison
!"
####
$
$
% $
$
&
££
The evolution of a system
'
%
|
Presentation
Application
Database
)*+
The Solution
archer10
Architectural Goals
• Structured
• Horizontal Scalability
• Availability & Reliability
• Visibility / Monitorability
• Flexibility & Replaceability
archer10
The Mandate!
• Service Oriented Architecture
• Domain Driven Design
• Micro-Services
• Continuous Delivery
• Oh and one more thing!
archer10
Domain Driven Design
• Entity
• Value Object
• Aggregate
• Service
• Repository
• Factory
• Bounded Context
archer10
Domain Driven Design
archer10
• Entity
• Value Object
• Aggregate
• Service
• Repository
• Factory
• Bounded Context
%
'
Service Oriented Architecture
% % ''
% '
Understand your Domain
!
####Home
JourneyPanel Quote
Engine
% % ''
"
Risk Enquiry Provider Quote
The End??
Just one more thing…http://m.cdn.blog.hu/ke/kedvessigmund/image/columbo.jpg
CQRS & Event Sourcing
• CQRS or “Why do we use the same schema for reads and writes?”
• Event Sourcing or “Why do we allow ORMs to dictate our object model?”
archer10
CQRS
Command Query Responsibility Segregation
,
, ,
, ,write
}read
}
UPDATE Address SET number = 10, street = ‘Downing Street’, city = ‘London’, postcode = ‘SW1A 2AA’ WHERE _id = ‘590b9902’
{ “_id” : “590b9902”, “event” : “PolicySpecified”, “data” : { “type” : “ContentsInsurance”, “excess” : 750 } }
{ “_id” : “590b9902”, “event” : “AddressModified”, “data” : { “number” : 10, “street” : “Downing Street”, “city” : “London”, “postcode” : “SW1A 2AA” } }
Use SQL?
-
,./
%
/
/ / / / /
public class Customer : AggregateRoot { private readonly Guid id; ! private Address currentAddress; // Business Logic public void ChangeAddress(Address newAddress) { if (newAddress.IsValid()) { Raise(new AddressModified(id, newAddress)); } } ! // State Transition public void Apply(AddressModified @event) { currentAddress = @event.Address; } !! ...... !! // Business Logic public void SpecifyCommunicationPreferences(CommunicationPrefs preferences) { Raise(new CommunicationPreferencesSpecified(id, preferences));
Event Sourcing (C#)
Eventual Consistency
% '
!
-
,.
/ / / / /
POST /risk/new GET /risk/590b9902:v1
/
HTTP/1.1 302 !Location: /risk/590b9902:v1
0
What we ended up with:% '-
% '-
% '-
% '-
% '-
# # # # # # # # # #
)* +
1
2 % '-
2% '-
2
The Conclusion
tim_norris
Fully Operational
Simple
Commitment
Metaphor
Discipline
Serendipity
3 +?
3?3
The Power!
public class Customer : AggregateRoot { private readonly Guid id; ! private Address currentAddress; ! public void ChangeAddress(Address newAddress) { Raise(new AddressModified(id, newAddress)); } ! public void Apply(AddressModified @event) { currentAddress = @event.Address; } }
The Power! public class Customer : AggregateRoot { private readonly Guid id; ! private Address currentAddress; ! public void ChangeAddress(Address newAddress) { if (currentAddress.IsSignificantlyDifferentFrom(newAddress)) { Raise(new CustomerMoved(id, newAddress)); } else { Raise(new AddressModified(id, newAddress)); } } ! public void Apply(CustomerMoved @event) { currentAddress = @event.Address; } ! public void Apply(AddressModified @event) { currentAddress = @event.Address; } }
The Flexibility!
var connection = rabbit.createConnection({ url: ‘amqp://localhost:5672' }) !connection.on('ready', function () { console.info('Connected'); exchange = connection.exchange('SOURCE:Exchange', { 'type': 'topic', durable: true }, function () { var queue = connection.queue('PROJECTION:Map', { durable: false, exclusive: true }, function () { console.info("Joined Queue"); queue.subscribe(function (message, headers, deliveryInfo) { var policyDetails = JSON.parse(message.data.toString()); io.sockets.emit('postcode', policyDetails.Address.Postcode); }); queue.bind(exchange.name, 'AddressDetailsSpecified'); }); queue.on('queueBindOk', function () { console.info('Bound queue to exchange'); }); }); !});
Is it a silver bullet?
rob.knight
Lessons Learnt
• Prefer simpler communication protocols
• Messaging == Push == Transient,
• HTTP == Pull == Permanent
• Difficult to evolve Domain Events
• Events as the system contract
Thank You!!
Tom Scott @tomwscott