4Developers 2015: CQRS - Prosta architektura dla nieprostego systemu! - Mateusz Stasch

Post on 15-Jul-2015

112 views 0 download

Transcript of 4Developers 2015: CQRS - Prosta architektura dla nieprostego systemu! - Mateusz Stasch

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