Loading presentation...
Prezi is an interactive zooming presentation

Present Remotely

Send the link below via email or IM

Copy

Present to your audience

Start remote presentation

  • Invited audience members will follow you as you navigate and present
  • People invited to a presentation do not need a Prezi account
  • This link expires 10 minutes after you close the presentation
  • A maximum of 30 users can follow your presentation
  • Learn more about this feature in our knowledge base article

Do you really want to delete this prezi?

Neither you, nor the coeditors you shared it with will be able to recover it again.

DeleteCancel

Make your likes visible on Facebook?

Connect your Facebook account to Prezi and let your likes appear on your timeline.
You can change this under Settings & Account at any time.

No, thanks

Command-query Responsibility Segregation - nowe, bardziej racjonalne podejście do warstw

CqRS, Domain Driven Design, Event Sourcing, etc
by

Sławek Sobótka

on 10 May 2011

Comments (0)

Please log in to add your comment.

Report abuse

Transcript of Command-query Responsibility Segregation - nowe, bardziej racjonalne podejście do warstw

Trener i konsultant


Community



Twórca oprogramowania – całościowe podejście
Command-query
Responsibility Segregation

Nowe, bardziej racjonalne
podejście do warstw
O sobie
Sławomir Sobótka
Java EE
Inżynieria oprogramowania
prezes Stowarzyszenia Software Engineering Professionals Polska
blogger, autor
członek rady programowej 4Developers
Technologie
Architektury
Najlepsze praktyki
Wzorce
Metodyki
Software
Craftsmanship
Klasa
problemu
Klasyfikowanie problemów Refleksja nad rozwiązaniami Krytyczne myślenie Poszukiwanie nowych rozwiązań Dobór narzędzia do problemu
Architektura
Aplikacji
Systemu
Wdrożenia
Bezpieczeństwa
Ewolucja Architektury
?
Procesy ewolucyjne są ślepe. Czasem osiągają lokalne maksimum, z którego nie mogą się już wydostać.

Richard Dawkins
Architektura 3-warstwowa
Warstwy są dobre, bo:
Ogry mają warstwy
Architektury aplikacji
Microkernel
Pipes and Filters
Blackboard
Layers

Layer != Tier
Separacja
odpowiedzialności
Jeżeli ktoś rozróżnia naukę (poznanie) od zabawy, to nie zrozumiał żadnego z tych pojęć.
Dostęp do danych
Logika
Prezentacja
Data Access Objects
(aka Repositories)
Metody CRUD
+ 150 metod wyszukujących
Zwracamy Encje
(attached) - czasem nawet do Prezentacji
n+1 Select Problem
przypadkowy update
stan na kliencie
...
Zwracamy model,
który czasem nie nadaje się do Prezentacji
Model biznesowy jest zwykle nierelewantny z widokiem
Potrzeba projekcji, wyliczenia na podstawie sesji
Niepotrzebne dane
Serwisy
(aka turbopascalowe Procedury)
(aka 8-tysięczniki)
Zmiana wymaga wspinaczki na 5. tysiąc i założenia tam bazy na kilka dni
Mix poziomów abstrakcji - przeładowanie mentalne
Core biznesu i setki rzeczy dodatkowych, wykonywanych czasem
Czasem jedynie przepakowanie Encji na DTO
(serwisy-wyszukiwarki)
Metody typu save(...)
Nie wyrażają intencji
Złożona logika sprawdzania tego co właściwie zapisujemy
Przeniesienie logiki biznesowej do widoku
Transaction Script
TS jest wzorcem!
Nie zawsze jest to anty-wzorzec!
Jest odpowiedni do prostych procedur (kilka warunków, proste kalkulacje)
Nie zawsze jest to problem
W pewnych klasach systemów
wszystko jest OK
Kontrolery/Akcje/ManagedBeany
GUI jest odbiciem tabel - przeglądarka danych
GUI nastawione na sekwencję operacji CRUD
Pojawia się logika biznesowa
Pojawia się zarządzanie stanem aplikacji
Settery uruchamiane nie tylko po to aby zebrać dane
Również w celu "wykonania" logiki
np. anulowanie zamówienia - setStatus :)
Stan modelu na warstwie prezentacji reprezentuje stan procesu biznesowego
Testowanie w najtrudniejszym środowisku jakie można sobie wyobrazić
Dodatkowa "warstwa"
Czasem jest to nawet overengineering
1. Rozwarstwienie logiki
Prezentacja
Logika Aplikacji
Logika Biznesowa
Infrastruktura
Serwisy aplikacyjne
cienka warstwa
orkiestracja obiektów biznesowych
technikalia: transakcje, bezpieczeństwo,...
Rich Domain
Domain Driven Design
Building Blocks
Entity
Aggregate
Aggregate
Value Object
Serwis (biznesowy)
Policy
rich, not anemic
odpowiedzialność charakterystyczna dla danego bytu
graf encji
enkapsulacja
jednostka logiki biznesowej
wrapper dla typów prostych
zwiększenie ekspresji modelu biznesowego
uwypuklenie koncepcji biz.
koniec z utilsami:)
w odróżnieniu od aplikacyjnego
czynność, która nie jest charakterystyczna dla żadnego Agregatu/Encji/VO
uwypuklenie koncepcji biznesowej
technicznie: Strategy Design Pattern
uwypuklenie wariacji procesu biznesowego
IoC na poziomie logiki biz.
komunikacja pomiędzy Bounded Context
Zdarzenie (wydarzenie) biznesowe
Factory
tworzy złożone agregaty - również część logiki biz.
dba o poprawność - nie istnieją obiekty w niepoprawnym stanie
technicznie: drastycznie zwiększa testability
zarządza trwałością Agregatu/Encji
nie zawiera 150 wyszukiwarek (tak jak DAO)
ew. wyszukiwanie biznesowe
Repository
2. Paradygmat
Command-query Separation
Metoda klasy może być jednym z dwóch:
poleceniem - zmienia stan
kwerendą - odpytuje o stan
nie zmienia stanu
wielokrotne wywołanie nie narusza stanu
Naruszenie CqS
public int załatwSprawę(){
pole++;
return pole;
}
Uogólnienie do poziomu
architektury aplikacji
Zmiana
paradygmatu
3. Dwa stosy warstw
Command
Query
Silny mutagen!!!
Klient
Command
public class AddProductToOrder{
private Long orderId;
private Long productId;
private int quantity;

//konstruktor
//gettery
}
Serwer
Serwis
@Transactional
@Service
//...
public class OrderService{
@Autowired
private OrdersRepository ordersRepository;
@Autowired
private PreductsRepository productssRepository;

public void addProductToOrder(AddProductToOrderCommand command){
Order order = ordersRepository.get(command.getOrderId());
Product product = productssRepository.get(command.getProductId());

order.addProduct(product, command.getQuantity());

ordersRepository.save(order);
}

//albo nawet klasycznie
public void addProductToOrder(Long orderId, Long productId, int quantity){

}
}
Command Handler
public class AddItemToOrderCommandhandler extends
Commandhandler<AddItemToCartCommand>{
private OrdersRepository;
private ProductsRepository

public void handle(AddItemCoCartCommand command){

}
}
Command
to
Hander
Matcher
Nie potrzebujemy frameworków!
Security Decorator
Injecting Decorator
Transaction Decorator
Command
Handler
Repository
Utrwalenie/pobranie Agregatu/Encji
ORM jest wygodny i produktywny
Odpowiednie narzędzenie do odpowiedniej klasy problemu
Baza
Przechowuje model biznesu
relacyjne podejście
III postać normalna itd.
ew. baza obiektowa
Kolejka Komend
np.: klienty (nie klienci) typu:
AJAX
Mobile
usprawnienia:
priorytety
zapamiętywanie x ostatnich
aby odrzucać duplikaty
Logika domenowa
Agregat
Główna jednostka pracy
Klient
Serwer
Wydarzenia Biznesowe
public class OrdersQuery impements Serializable{
private OrderStatus status
private DateRange createDate
//...

//fluent query a'la DSL
public OrdersQuery expiring(){
status = DRAFT;
createDate.begin = Date.Now.substractDays(30);
return this;
}
}
Query
DTO
"szyte na miarę" widoku/UseCase
zawiera tylko potrzebne dane
Serwis (Finder)
Cienka warstwa
Jedynie konstruuje zapytanie i zwraca dane
Nie koniecznie ORM, wystarczy JDBC, myBatis,...
Baza
Poziomy separacji
Ten sam model (III postać)
Ta sama baza - widoki, denormalizacja
Inny model
noSQL?
nieadekwatny do prezentacji
kłopotliwe JOINY
...
4. Drugi wymiar - czas
Odpowiedzialność
t
Zdarzenia (Wydarzenia biznesowe)
Modelowanie czasu
Klient
Command
Agregat
Serwis/Handler
Wydarzenie biznesowe
(fakt, który nieodwracalnie miał miejscje)
Listener 1
Write Model
Read Model
(denormalizacja)
Queue
Finder
Query
DTO
SELECT * FROM X WHERE ...
Where are JOINs?
Dane po opuszczeniu transakcji
są i tak nieświeże
Niemal dowolne
skalowanie liniowe
stosu odczytu!
Separacja kompetencji
Stos CRUD
W dużym systemie
na pewno
niemała część funkcjonalności
będzie klasy CRUD
materia

miękka
Łatwe
wersjonowanie
i podmiana
Handlerów
Architektura po wprowadzeniu Zdarzeń
wygląda na skomplikowaną
ale upraszcza szczegóły
Client
Write
Read
Command
DTO
(Event)
najlepsi ludzie
skill analityczny (w tym miękki)
dociekliwość, stawianie pytań
wiedza domenowa
nie potrzeba wiedzy domenowej
(Command i DTO)
empatia - koniec z autystycznym GUI
skillset z zakresu usability
poczucie estetyki
1x DB Masta Hacka
Juniors
Jeżeli robisz coś i zaczyna być to skomplikowane, to robisz to źle.
Sławomir Sobótka
slawomir.sobotka@bottega.com.pl
http://art-of-software.blogspot.com/


http://ssepp.pl

public class Order extends AggregateRoot{
....private Status status;
....private Payment payment;

....public void confirm(Payment payment){
........//logika biznesowa

........if (statusAllowsConfirmation(status)){
............fireEvent(new OrderConfirmed(id, payment)); //id z super-klasy
........}
....}
}
Listener 2
Agregat - klasyczny
public class Client extends AggregateRoot {
....public void changeStatus(ClientStatus status){
........this.status = status;
........//wiecej logiki biznesowej

........if (status == VIP){
............//rabat na niezrealizowane zamówienia
........}
....}
}
wydarzenie biznesowe z kontekstu obsługi klienta
listener operujący w kontekście sprzedaży
DDD nie jest wymagane dla CqrS
Ale jak inaczej?
.
Klasyczna patologia - nie wiem co dzieje się w studnaich obok
Ewolucyjne ujęcie architektury aplikacji
Read Model
(denormalizacja)
Składowanie zdarzeń - model behawioralny
Wydarzenie biznesowe
(modelujemy przejścia stanów)
SELECT * FROM X WHERE ...
public class Order extends AggregateRoot{
....private Status status;
....private Payment payment;

....//zmiana stanu
....private void handleEvent(OrderConfirmed event){
........status = CONFIRMED;
........payment = event.getPayment();
....}

....public void confirm(Payment payment){
........//jakas pomocnicza metoda - logika biznesowa
........if (statusAllowsConfirmation(status)){
............fireEvent(new OrderConfirmed(id, payment)); //id z super-klasy
........}
....}
}
Queue
Klient
Teraz będzie hardkorowo
public class AggregateRoot{
....private List<Event> changes;

....public void load(List<Event> events){
........for(Event event : events){
............fireEvent(event, false);
........}
....}

....protected void fireEvent(Event event){
........fireEvent(event, true);
....}
....private void fireEvent(Event event, boolean isChange){
........//"wydłubanie" handlera (np odpowiedniej metody)
........if (isChange)
.............changes.add(event);
....}
}
Where are JOINs?
Listener 3
Finder
Zapisz wydarzenie
Dane po opuszczeniu transakcji
są i tak nieświeże
5. Event Sourcing
Command
Write Model
(Event Storage)
Query
Niemal dowolne
skalowanie liniowe
stosu odczytu!
Agregat
Serwis/Handler
DTO
Pobierz Agregat w stanie z chwili X
(odtworzenie stanu na podstawie wydarzeń)
Uaktualnienie modelu odczytu
Integracja z innymi modułami
Integracja z innymi modułami
Listener 1
wydarzenie biznesowe z kontekstu obsługi klienta
listener operujący w kontekście sprzedaży
Agregat - klasyczny
Uaktualnienie modelu odczytu
public class Client extends AggregateRoot {
....public void changeStatus(ClientStatus status){
........this.status = status;
........if (status == VIP){
............//rabat na niezrealizowane zamówienia
........}
....}
}
Listener 2
Zapis wydarzeń
(serializowane do XML/JSON obiekty)
Full transcript