From CRUD to messages…a true story…
…il contesto…
• Applicazione “finanziaria”
• Circa 70 (230) utenti
• 137.000+ (1.000.000+) aziende
• 420.000+ (4.500.000+) bilanci
• 38.000.000+ (650.000.000+) voci di bilancio
• 16.000+ (85.000+) call/mese
• “Pluggato” a servizi di terze parti
• Picchi di lavoro “temporali”
Pre
sen
tati
on
Lay
er
Serv
ice/
Ap
plic
atio
n L
ayer
Do
mai
n M
od
el
Storage
public interface ICompanyDossierService
{
void CloseCompanyFile(Guid id);
void SetInCharge(Guid id);
void SetAnalysisInCharge(Guid id);
IEnumerable<CompanyDossier> Search(Int16[] statuses, ...);
Guid Save(Guid companyFileId, String description, ...);
void DeleteAttachment(Guid id);
AttachmentStream GetAttachmentStream(Guid id);
...
}
ANY PROBLEM?
• Logica (mal) sparpagliata
• Contesti non definiti
• Modello “fragile” e poco propenso alle
evoluzioni
• Un unico modello per la lettura e per la
scrittura
• Prestazioni
• UI “generazionalista”
An object model of the domain that
incorporates both behavior and data
[Martin Fowler - http://martinfowler.com/eaaCatalog/domainModel.html]
Use aggregates as unit of
consistency
across your domain model
Protect your model with clearly
defined bounded context
Eric Evans
Use aggregates as unit of
consistency
across your domain model
Protect your model with clearly
defined bounded context
Eric Evans
Use aggregates as unit of
consistency
across your domain model
Protect your model with clearly
defined bounded context
Eric Evans
A single model cannot be appropriate for
reporting, searching and transactional
behavior
Greg Young
Pre
sen
tati
on
Lay
er
Serv
ice
Laye
r
Do
mai
n M
od
el
Write storage
Read storage
State transition are an important part of our
problem space and should be modeled
within our domain
Greg Young, 2008
{
"_id" : {
"BucketId" : "Documents/Type",
"StreamId" : "1",
"CommitSequence" : 1
}
"CheckpointNumber" : NumberLong(9460),
"CommitId" : LUUID("5aeae871-8a94-294a-88b1-b2487e5d88d6"),
"CommitStamp" : ISODate("2013-11-21T14:58:08.500Z"),
"Dispatched" : true,
"Events" : [ {
"StreamRevision" : 1,
"Payload" : {
”Code" : ”SITBNC",
”Name" : "Situazione bancaria”,
“Enabled” : true,
“Metadata” : []
}
}],
"Headers" : {
"who" : "guest",
"when" : "2013112115580858",
"when/ticks" : NumberLong(635206462884868984),
"correlation" : "230b5b33-a2ce-4f7e-aa4e-a885c3f05274”
}
}
It’s really become clear to me in the last
couple of years that we need a new building
block and that is the domain events
Eric Evans
It’s really become clear to me
in the last couple of years
that we need a new building block
and that is the domain events
Eric Evans
Pre
sen
tati
on
Lay
er
Serv
ice
Laye
r
Do
mai
n M
od
el
Write storage
Read storage
CONTACT CENTER FINANCE
Compute Ratings
Command
Appointment
Reserved
class ComputeRatingsCommandActivator :
IConsumer<AppointmentReserved>
{
public async Task Consume(
ConsumeContext<AppointmentReserved> context)
{
// COLLECT INFOS AND INITIALIZE THE COMMAND
var endpoint =
await context.GetSendEndpoint(“MyCommandEndpoint”);
endpoint.Send(command);
}
}
CONTACT CENTER
FINANCE
Compute Ratings
Command
Appointment
Reserved
CONSULTANT
Reserve Slot
Command
Appointment
Reserved
Appointment
Reserved
class MachineInstance : SagaStateMachineInstance
{
public Guid CorrelationId { get; private set; }
public State CurrentState { get; private set; }
public Boolean ConsultantCommandResponse { get; set; }
public Boolean FinanceCommandResponse { get; set; }
}
class StateMachine : MassTransitStateMachine<MachineInstance>
{
public StateMachine()
{
// MASSTRANSIT’S DSL TO DEFINE SAGA
}
public State Running { get; set; }
public Event<AppointmentActivated>
AppointmentActivated { get; private set; }
public Event<ConsultantCommandExecuted>
ConsultantCommandExecuted { get; private set; }
public Event<FinanceCommandExecuted>
FinanceCommandExecuted { get; private set; }
}
GOOD / BAD
CONTACT CENTER
FINANCE
Compute Ratings
Command
State Transition
Happened
CONSULTANT
Reserve Slot
Command
Resource
Changed
Resource
Changed
Resource
Changed
CONTACT CENTER
FINANCE
CONSULTANT
class ComputeRatingsCommandActivator :
IConsumer<AppointmentReserved>
{
public async Task Consume(
ConsumeContext<AppointmentReserved> context)
{
// COLLECT INFOS AND INITIALIZE THE COMMAND
var endpoint =
await context.GetSendEndpoint(“MyCommandEndpoint”);
endpoint.Send(command);
}
}
REST (HTTP)
TO THE RESCUE
class ComputeRatingsCommandActivator :
IConsumer<AppointmentReserved>
{
public async Task Consume(
ConsumeContext<AppointmentReserved> context)
{
// USE API (MICROSERVICE?) TO COLLECT INFOS
// AND INITIALIZE THE COMMAND
var endpoint =
await context.GetSendEndpoint(“MyCommandEndpoint”);
endpoint.Send(command);
}
}
> GET /api/appointments/13
< 200 Ok
< ETag: 686897696a7c876b7e
< Last-Modified: Thu, 05 Jul 2012 15:31:30
GMT
> GET /api/appointments/13
< 301 Moved permanently
< Location: http://...
DEPLOY?
SONO UNA CAGATA PAZZESCA!!!
I MICROSERVICES…
WHEN?Tackling complexity in the heart of software
WHEN?
Tackling complexity in the
heart of software