Upload
proidea
View
113
Download
0
Embed Size (px)
Citation preview
Prosta architektura dla nieprostego systemu
Mateusz Stasch
Mateusz Stasch
@mattstasch
http://mattstasch.net
CQRS
CQRS
CQRSjest trudny
CQRSjest trudny *
CQRS
CQRSImplementacja zasady CQS
w postaci wzorca architektonicznego*
DBModelBusiness
LogicUI
DTO
DTO
ORM
DBDomain Model
Commands
UI
DTO
DTO
ORM
Queries
DB
WRITE Domain ModelCommands
UI
DTO
DTO
ORM
Queries READ Model
DB
WRITE Domain ModelCommands
UI
DTO
DTO
ORM
Queries
SELECT … FROM … WHERE …
READ Model
READ DB
WRITE Domain ModelCommands
UI
DTO
DTO QueriesSELECT … FROM … WHERE …
WRITE DB
PROJECTION
READ DB
WRITE Domain ModelCommands
UI
DTO
DTO QueriesSELECT … FROM … WHERE …
PROJECTION
Event Stream
CQRS != ES
public class UserManager : IUserManager{
public UserDetails UpdateUserStatus(...) {
// ...
return user; }
}
public class UserManager : IUserManager{
public UserDetails UpdateUserStatus(...) {
// ...
return user; }
}
RESPONSIBILITY?
Manager
Command – zmienia stan systemu
Query – odczytuje stan systemu
public interface IQuery<out TResult>{
TResult Execute();}
public interface IQuery<out TResult>{
TResult Execute();}
public class QueryDispatcher : IQueryDispatcher{
TResult Run<TResult>(IQuery<TResult> query){
using(var tx = session.OpenReadOnlyTransaction()){
return query.Execute();}
}}
public interface IQuery<out TResult>{
TResult Execute();}
public class QueryDispatcher : IQueryDispatcher{
TResult Run<TResult>(IQuery<TResult> query){
using(var tx = session.OpenReadOnlyTransaction()){
return query.Execute();}
}}
public interface ICommandHandler<in TCommand>{
void Execute(TCommand command);}
public interface ICommandHandler<in TCommand>{
void Execute(TCommand command);}
public class CommandDispatcher : ICommandDispatcher{
public void Execute<in TCommand>(TCommand command){
var handler = ResolveCommandHandler<TCommand>();
handler.Execute(command);}
}
public interface ICommandHandler<in TCommand>{
void Execute(TCommand command);}
public class CommandDispatcher : ICommandDispatcher{
public void Execute<in TCommand>(TCommand command){
var handler = ResolveCommandHandler<TCommand>();
handler.Execute(command);}
}
Semantyka
Ekspresywność kodu
Semantyka
Ekspresywność kodu
Semantyka
Single Responsibility
using(var tx = session.BeginTransaction()){
try{
var user = repository.GetById<UserDetails>(id);
user.Status = newStatus;
tx.Commit();
return user;}catch{
tx.Rollback();throw;
}}
public class UserManager : IUserManager{
[Transactional]public UserDetails UpdateUserStatus(...) {
// ...
return user; }
}
using(var tx = session.BeginTransaction()){
try{
var user = repository.GetById<UserDetails>(id);
user.Status = newStatus;
tx.Commit();
return user;}catch{
tx.Rollback();throw;
}}
using(var tx = session.BeginTransaction()){
try{
var user = repository.GetById<UserDetails>(id);
user.Status = newStatus;
tx.Commit();
return user;}catch{
tx.Rollback();throw;
}}
public UpdateUserStatusCommandHandler: ICommandHandler<UpdateUserStatusCommand>
{public void Execute(UpdateUserStatusCommand command){
var user = repository.GetById(command.Id);user.Status = command.NewStatus;
}}
public class CommandDispatcher : ICommandDispatcher{
public void Execute<in TCommand>(TCommand command){
var handler = ResolveCommandHandler<TCommand>();using(var tx = session.OpenTransaction()){
try{
handler.Execute(command);tx.Commit();
}catch{
tx.Rollback();throw;
}}
}}
public class TransactionalCommandDispatcherDecorator : ICommandDispatcher{
public TransactionalCommandDispatcher(ICommandDispatcher …){…}
public void Execute<in TCommand>(TCommand command){
using(var tx = session.OpenTransaction()){
try{
decoratedDispatcher.Execute(command);tx.Commit();
}catch{
tx.Rollback();throw;
}}
}}
Logika biznesowa
Infr
astr
ukt
ura
Database
Infrastructure
Domain
Business Logic
UI
Domena
Database
Infrastructure
Domain
Querying
UI
Database
Infrastructure
Domain
Querying
UI
De
pe
nd
en
cy
In
je
ct
io
n
IQuery
IQueryDispatcher
GetUserDetailsQuery
Database
Infrastructure
Domain
Querying
UI
IUserRepository
ICommand ICommandHandler
ICommandDispatcher
De
pe
nd
en
cy
In
je
ct
io
n
IQuery
IQueryDispatcher
UpdateStatusCommand(Handler)
GetUserDetailsQuery
Database
Infrastructure
Domain
Querying
UI
IUserRepository
ICommand ICommandHandler
ICommandDispatcher
De
pe
nd
en
cy
In
je
ct
io
n
IQuery
IQueryDispatcher
UpdateStatusCommand(Handler)
CommandDispatcherUserRepository
QueryDispatcher
GetUserDetailsQuery
Domain
Infrastructure
ICommandDispatcher
CommandDispatcher
ICommand
DeactivateUserCommand
IQuery
QueryDispatcher
IUserRepository
UserRepository
…
using(var tx = session.BeginTransaction()){
try{
var user = repository.GetById<UserDetails>(id);
user.Status = newStatus;
tx.Commit();
return user;}catch{
tx.Rollback();throw;
}}
public UpdateUserStatusCommandHandler: ICommandHandler<UpdateUserStatusCommand>
{// ...public void Execute(UpdateUserStatusCommand command){
var user = repository.GetById(command.Id);user.Status = command.NewStatus;
}}
public UpdateUserStatusCommandHandler: ICommandHandler<UpdateUserStatusCommand>
{// ...public void Execute(UpdateUserStatusCommand command){
var user = repository.GetById(command.Id);user.Status = command.NewStatus;
}}
Robienie CRUDa w CQRSie jest bezsensowne!
Robienie CRUDa w CQRSie jest bezsensowne!
public DeactivateUserCommandHandler: ICommandHandler<DeactivateUserCommand>
{// ...public void Execute(DeactivateUserCommand command){
var user = repository.GetById(command.Id);
user.Deactivate();}
}
READ DB
WRITE Domain ModelCommands
UI
ORM
QueriesSELECT … FROM … WHERE …
WRITE DB
PROJECTION
READ DB
WRITE Domain ModelCommands
UI
ORM
QueriesSELECT … FROM … WHERE …
WRITE DB
PROJECTION
READ DB
READ DB WRITE DB!=
READ DB WRITE DB!=
Eager Read Derivation
READ DB ==
READ DB
READ DB
READ DB
READ DB
WRITE Domain ModelCommands
UI
ORM
QueriesSELECT … FROM … WHERE …
WRITE DB
PROJECTION
Eventual Consistency
DB
WRITE Domain ModelCommands
UI
ORM
Queries
SELECT … FROM … WHERE …
READ Model
Jak zacząć?
Jakiś Framework?
Nope!
Napisz to sam!
To proste!
public interface IQuery<out TResult>{
TResult Execute();}
public class QueryDispatcher : IQueryDispatcher{
TResult Run<out TResult>(IQuery<TResult> query){
using(var tx = session.OpenReadOnlyTransaction()){
return query.Execute();}
}}
public interface IQuery<out TResult>{
TResult Execute();}
public class QueryDispatcher : IQueryDispatcher{
TResult Run<out TResult>(IQuery<TResult> query){
using(var tx = session.OpenReadOnlyTransaction()){
return query.Execute();}
}}
public interface ICommandHandler<in TCommand>{
void Execute(TCommand command);}
public class CommandDispatcher : ICommandDispatcher{
public void Execute<in TCommand>(TCommand command){
var handler = ResolveCommandHandler<TCommand>();
handler.Execute(command);}
}
public class TaskController{
public TaskController(TaskManager taskManager) {…}
public ActionResult MarkAsFinalized(TaskId taskId){
taskManager.UpdateStatus(taskId, TaskStatuses.Finalized);taskManager.UpdateFinalizationDate(taskId, DateTime.Now);taskManager.UpdateFinalizedBy(taskId, CurrentUser.Id);
return Success;}
}
public class TaskController : Controller{
public TaskController(CommandDispatcher commandDispatcher) {…}
public ActionResult MarkAsFinalized(TaskId taskId){
var finalizeCommand = new FinalizeTaskCommand(taskId, CurrrentUser.Id);
commandDispatcher.Execute(finalizeCommand);
return Success;}
}
public class FinalizeTaskCommand{
public FinalizeTaskCommand(TaskId id, UserId finalizingUser) {…}
public TaskId Id { get; private set; }
public UserId FinalizingUser { get; private set; }}
public class FinalizeTaskCommandHandler: ICommandHandler<FinalizeTaskCommand>
{public FinalizeTaskCommandHanlder(
ITaskRepository repository,IDateTimeProvider dtp) {…}
public void Execute(FinalizeTaskCommand command){
var task = repository.GetById(command.Id);
task.Status = TaskStatuses.Done;task.FinalizedAt = dtp.Now();task.FinalizedBy = command.FinalizingUser;
}}
public class FinalizeTaskCommandHandler: ICommandHandler<FinalizeTaskCommand>
{public FinalizeTaskCommandHanlder(
ITaskRepository repository,IDateTimeProvider dtp) {…}
public void Execute(FinalizeTaskCommand command){
var task = repository.GetById(command.Id);
task.Finalize(dtp.Now(), command.FinalizingUser);}
}
Q&AProsta architektura
dla nieprostego systemu
Mateusz Stasch
Dziękuję za uwagę
Prosta architektura dla nieprostego systemu
Mateusz Stasch