Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Prosta architektura
dla nieprostego systemu
Mateusz Stasch
Mateusz Stasch
@mattstasch
http://mattstasch.net
CQRS
CQRS
CQRS
jest trudny
CQRS
jest trudny *
CQRS
CQRS
Implementacja zasady CQS
w postaci wzorca architektonicznego*
DBModel
Business
Logic
UI
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 Queries
SELECT … FROM … WHERE …
WRITE DB
PROJECTION
READ DB
WRITE Domain ModelCommands
UI
DTO
DTO Queries
SELECT … 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<...
public interface IQuery<out TResult>
{
TResult Execute();
}
public class QueryDispatcher : IQueryDispatcher
{
TResult Run<...
public interface ICommandHandler<in TCommand>
{
void Execute(TCommand command);
}
public interface ICommandHandler<in TCommand>
{
void Execute(TCommand command);
}
public class CommandDispatcher : IComman...
public interface ICommandHandler<in TCommand>
{
void Execute(TCommand command);
}
public class CommandDispatcher : IComman...
Semantyka
Ekspresywność kodu
Semantyka
Ekspresywność kodu
Semantyka
Single Responsibility
using(var tx = session.BeginTransaction())
{
try
{
var user = repository.GetById<UserDetails>(id);
user.Status = newStatus...
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...
using(var tx = session.BeginTransaction())
{
try
{
var user = repository.GetById<UserDetails>(id);
user.Status = newStatus...
public UpdateUserStatusCommandHandler
: ICommandHandler<UpdateUserStatusCommand>
{
public void Execute(UpdateUserStatusCom...
public class CommandDispatcher : ICommandDispatcher
{
public void Execute<in TCommand>(TCommand command)
{
var handler = R...
public class TransactionalCommandDispatcherDecorator : ICommandDispatcher
{
public TransactionalCommandDispatcher(ICommand...
Logika biznesowa
Infrastruktura
Database
Infrastructure
Domain
Business Logic
UI
Domena
Database
Infrastructure
Domain
Querying
UI
Database
Infrastructure
Domain
Querying
UI
DependencyInjection
IQuery
IQueryDispatcher
GetUserDetailsQuery
Database
Infrastructure
Domain
Querying
UI
IUserRepository
ICommand ICommandHandler
ICommandDispatcher
DependencyInjection...
Database
Infrastructure
Domain
Querying
UI
IUserRepository
ICommand ICommandHandler
ICommandDispatcher
DependencyInjection...
Domain
Infrastructure
ICommandDispatcher
CommandDispatcher
ICommand
DeactivateUserCommand
IQuery
QueryDispatcher
IUserRepo...
using(var tx = session.BeginTransaction())
{
try
{
var user = repository.GetById<UserDetails>(id);
user.Status = newStatus...
public UpdateUserStatusCommandHandler
: ICommandHandler<UpdateUserStatusCommand>
{
// ...
public void Execute(UpdateUserSt...
public UpdateUserStatusCommandHandler
: ICommandHandler<UpdateUserStatusCommand>
{
// ...
public void Execute(UpdateUserSt...
Robienie CRUDa w CQRSie jest bezsensowne!
Robienie CRUDa w CQRSie jest bezsensowne!
public DeactivateUserCommandHandler
: ICommandHandler<DeactivateUserCommand>
{
// ...
public void Execute(DeactivateUserCo...
READ DB
WRITE Domain ModelCommands
UI
ORM
Queries
SELECT … FROM … WHERE …
WRITE DB
PROJECTION
READ DB
WRITE Domain ModelCommands
UI
ORM
Queries
SELECT … 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
Queries
SELECT … 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<...
public interface IQuery<out TResult>
{
TResult Execute();
}
public class QueryDispatcher : IQueryDispatcher
{
TResult Run<...
public interface ICommandHandler<in TCommand>
{
void Execute(TCommand command);
}
public class CommandDispatcher : IComman...
public class TaskController
{
public TaskController(TaskManager taskManager) {…}
public ActionResult MarkAsFinalized(TaskI...
public class TaskController : Controller
{
public TaskController(CommandDispatcher commandDispatcher) {…}
public ActionRes...
public class FinalizeTaskCommand
{
public FinalizeTaskCommand(TaskId id, UserId finalizingUser)
{…}
public TaskId Id { get...
public class FinalizeTaskCommandHandler
: ICommandHandler<FinalizeTaskCommand>
{
public FinalizeTaskCommandHanlder(
ITaskR...
public class FinalizeTaskCommandHandler
: ICommandHandler<FinalizeTaskCommand>
{
public FinalizeTaskCommandHanlder(
ITaskR...
Q&A
Prosta architektura
dla nieprostego systemu
Mateusz Stasch
Dziękuję
za uwagę
Prosta architektura
dla nieprostego systemu
Mateusz Stasch
4Developers 2015: CQRS - Prosta architektura dla nieprostego systemu! - Mateusz Stasch
4Developers 2015: CQRS - Prosta architektura dla nieprostego systemu! - Mateusz Stasch
Upcoming SlideShare
Loading in …5
×

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

336 views

Published on

Mateusz Stasch

Language: Polish

Opowiem dlaczego użycie CQRSa (Command Query Responsibility Segregation) nie musi wiązać się z odprawianiem czarnej magii oraz dlaczego pragmatyzm jest najlepszym sposobem na ‘elegancki’ kod. Pokażę, że CQRS nie jest wydumanym, abstrakcyjnym podejściem do architektury, a jedynie sprawia że to co dziś przemycamy po cichu w naszych architekturach staje się otwarcie zdefiniowane i dobrze umiejscowione.

Published in: Software
  • Be the first to comment

  • Be the first to like this

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

  1. 1. Prosta architektura dla nieprostego systemu Mateusz Stasch
  2. 2. Mateusz Stasch @mattstasch http://mattstasch.net
  3. 3. CQRS
  4. 4. CQRS
  5. 5. CQRS jest trudny
  6. 6. CQRS jest trudny *
  7. 7. CQRS
  8. 8. CQRS Implementacja zasady CQS w postaci wzorca architektonicznego*
  9. 9. DBModel Business Logic UI DTO DTO ORM
  10. 10. DBDomain Model Commands UI DTO DTO ORM Queries
  11. 11. DB WRITE Domain ModelCommands UI DTO DTO ORM Queries READ Model
  12. 12. DB WRITE Domain ModelCommands UI DTO DTO ORM Queries SELECT … FROM … WHERE … READ Model
  13. 13. READ DB WRITE Domain ModelCommands UI DTO DTO Queries SELECT … FROM … WHERE … WRITE DB PROJECTION
  14. 14. READ DB WRITE Domain ModelCommands UI DTO DTO Queries SELECT … FROM … WHERE … PROJECTION Event Stream
  15. 15. CQRS != ES
  16. 16. public class UserManager : IUserManager { public UserDetails UpdateUserStatus(...) { // ... return user; } }
  17. 17. public class UserManager : IUserManager { public UserDetails UpdateUserStatus(...) { // ... return user; } }
  18. 18. RESPONSIBILITY?
  19. 19. Manager
  20. 20. Command – zmienia stan systemu Query – odczytuje stan systemu
  21. 21. public interface IQuery<out TResult> { TResult Execute(); }
  22. 22. 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(); } } }
  23. 23. 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(); } } }
  24. 24. public interface ICommandHandler<in TCommand> { void Execute(TCommand command); }
  25. 25. 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); } }
  26. 26. 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); } }
  27. 27. Semantyka
  28. 28. Ekspresywność kodu Semantyka
  29. 29. Ekspresywność kodu Semantyka Single Responsibility
  30. 30. using(var tx = session.BeginTransaction()) { try { var user = repository.GetById<UserDetails>(id); user.Status = newStatus; tx.Commit(); return user; } catch { tx.Rollback(); throw; } }
  31. 31. public class UserManager : IUserManager { [Transactional] public UserDetails UpdateUserStatus(...) { // ... return user; } }
  32. 32. using(var tx = session.BeginTransaction()) { try { var user = repository.GetById<UserDetails>(id); user.Status = newStatus; tx.Commit(); return user; } catch { tx.Rollback(); throw; } }
  33. 33. using(var tx = session.BeginTransaction()) { try { var user = repository.GetById<UserDetails>(id); user.Status = newStatus; tx.Commit(); return user; } catch { tx.Rollback(); throw; } }
  34. 34. public UpdateUserStatusCommandHandler : ICommandHandler<UpdateUserStatusCommand> { public void Execute(UpdateUserStatusCommand command) { var user = repository.GetById(command.Id); user.Status = command.NewStatus; } }
  35. 35. 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; } } } }
  36. 36. 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; } } } }
  37. 37. Logika biznesowa Infrastruktura
  38. 38. Database Infrastructure Domain Business Logic UI
  39. 39. Domena
  40. 40. Database Infrastructure Domain Querying UI
  41. 41. Database Infrastructure Domain Querying UI DependencyInjection IQuery IQueryDispatcher GetUserDetailsQuery
  42. 42. Database Infrastructure Domain Querying UI IUserRepository ICommand ICommandHandler ICommandDispatcher DependencyInjection IQuery IQueryDispatcher UpdateStatusCommand(Handler) GetUserDetailsQuery
  43. 43. Database Infrastructure Domain Querying UI IUserRepository ICommand ICommandHandler ICommandDispatcher DependencyInjection IQuery IQueryDispatcher UpdateStatusCommand(Handler) CommandDispatcher UserRepository QueryDispatcher GetUserDetailsQuery
  44. 44. Domain Infrastructure ICommandDispatcher CommandDispatcher ICommand DeactivateUserCommand IQuery QueryDispatcher IUserRepository UserRepository …
  45. 45. using(var tx = session.BeginTransaction()) { try { var user = repository.GetById<UserDetails>(id); user.Status = newStatus; tx.Commit(); return user; } catch { tx.Rollback(); throw; } }
  46. 46. public UpdateUserStatusCommandHandler : ICommandHandler<UpdateUserStatusCommand> { // ... public void Execute(UpdateUserStatusCommand command) { var user = repository.GetById(command.Id); user.Status = command.NewStatus; } }
  47. 47. public UpdateUserStatusCommandHandler : ICommandHandler<UpdateUserStatusCommand> { // ... public void Execute(UpdateUserStatusCommand command) { var user = repository.GetById(command.Id); user.Status = command.NewStatus; } }
  48. 48. Robienie CRUDa w CQRSie jest bezsensowne!
  49. 49. Robienie CRUDa w CQRSie jest bezsensowne!
  50. 50. public DeactivateUserCommandHandler : ICommandHandler<DeactivateUserCommand> { // ... public void Execute(DeactivateUserCommand command) { var user = repository.GetById(command.Id); user.Deactivate(); } }
  51. 51. READ DB WRITE Domain ModelCommands UI ORM Queries SELECT … FROM … WHERE … WRITE DB PROJECTION
  52. 52. READ DB WRITE Domain ModelCommands UI ORM Queries SELECT … FROM … WHERE … WRITE DB PROJECTION
  53. 53. READ DB
  54. 54. READ DB WRITE DB!=
  55. 55. READ DB WRITE DB!= Eager Read Derivation
  56. 56. READ DB == READ DB READ DB READ DB
  57. 57. READ DB WRITE Domain ModelCommands UI ORM Queries SELECT … FROM … WHERE … WRITE DB PROJECTION Eventual Consistency
  58. 58. DB WRITE Domain ModelCommands UI ORM Queries SELECT … FROM … WHERE … READ Model
  59. 59. Jak zacząć?
  60. 60. Jakiś Framework?
  61. 61. Nope!
  62. 62. Napisz to sam! To proste!
  63. 63. 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(); } } }
  64. 64. 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(); } } }
  65. 65. 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); } }
  66. 66. 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; } }
  67. 67. 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; } }
  68. 68. public class FinalizeTaskCommand { public FinalizeTaskCommand(TaskId id, UserId finalizingUser) {…} public TaskId Id { get; private set; } public UserId FinalizingUser { get; private set; } }
  69. 69. 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; } }
  70. 70. 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); } }
  71. 71. Q&A Prosta architektura dla nieprostego systemu Mateusz Stasch
  72. 72. Dziękuję za uwagę Prosta architektura dla nieprostego systemu Mateusz Stasch

×