3. Monolit - Big Ball of Mud
www.synthesis.co.za
Anti-pattern
● trudna do wyodrębnienia i zrozumienia
struktura (lub jej brak)
● hermetyzacja jest szczątkowa
● podatność na regresje
Przyczyny
● zmiana wymagań biznesowych
● niekompetencja
● zróbmy coś “na szybko”
● legacy code
Product owner - to ten gość w białym ;)
6. Monolit
Zalety:
● developowanie (początek projektu)
● testowanie
● debugowanie
● łatwość wdrażanie nowych wersji
● skalowanie horyzontalne
Wady:
● utrzymanie / rozwój
● wydajność
● niezawodność
● czas wdrażania nowych wersji
● skalowanie - ze względu na zużywane zasoby
● “stały” stack technologiczny
7. MonolithFirst by Martin Fowler (vs Stefan Tilkov)
MonolitFirst vs Don’t start with a monolith
https://martinfowler.com/bliki/MonolithFirst.html
https://martinfowler.com/articles/dont-start-monolith.html
8. Poznanie wymagań biznesowych
● Rozmawiać, rozmawiać, rozmawiać nie tylko między sobą w IT
● Położyć nacisk na to aby każdy w zespole miał pełną świadomość wymagań
biznesowych, ważne jest aby nowe osoby w pierwszej kolejności nabyły taką
wiedzę
● Event Storming (odkrywanie domeny)
9. Event Storming I
Podczas ES staramy się nie korzystać ze słownictwa
fachowego i języka ściśle specjalistycznego. Celem jest, aby
każdy rozumiał każdego, a używane określenia
funkcjonowały w ramach danej domeny biznesowej.
Kto:
● facilitator - prowadzący, znający technikę ES
● eksperci domenowi
● eksperci techniczni
Co:
● karteczki samoprzylepne (różnokolorowe), każdy kolor
symbolizuje coś innego: zdarzenia domenowe,
komendy, aktorzy, problemy, notatki itp
● tablica lub ściana aby je można było przykleić
10. Event Storming II
Korzyści:
● wymiana wiedzy za czym idzie zrozumienie domeny
● powstaje wspólny język zrozumiały dla wszystkich osób (nie tylko technicznych)
● dystrybucja wiedzy na zewnątrz działu IT
● dla nowych projektów: szybki proces wstępnego planowania
● dla trwających projektów: identyfikacja słabych/problematycznych miejsc w aplikacji
● integracja pomiędzy zespołami/działami
11. Modular monolit (gruboziarnista separacja)
Dodajemy ściśle określone granice pomiędzy różnymi modułami biznesowymi.
Zagadnienia do przemyślenia:
● rozbicie całej aplikacji na moduły/komponenty
● projekt odpowiedniej struktury katalogów
● izolacja zależności
○ zdefiniowanie publicznego interfejsu danego modułu
○ określenie właścicieli danych
○ każdy moduł operuje na własnych danych
Trzeba być konsekwentnym w przestrzeganiu zasad !!!
12. Modular monolit (gruboziarnista separacja)
Moduł nie posiada bezpośredniej wiedzy o żadnym innym module, nie ma powiązania z żadną
klasą pochodzącą z innego modułu ani nawet z żadnym interfejsem
Aby nasze moduły były odseparowane od siebie musimy wyjść poza Dependency Inversion.
● events (reagowanie na zmiany w innych modułach)
● shared kernel
● eventual consistency
14. Hexagonal - warstwy
● User interface (UI) - GUI, konsola
● Application core (CORE) - logika biznesowa. Używana przez warstwę UI. Serce naszej aplikacji.
● Infrastructure (I) - zapewnia połączenie z zewnętrznymi mechanizmami np. baza danych, zewnętrzne api
Przepływ żądania:
UI -> Application -> Infrastructure -> Application -> Ui
15. Hexagonal - Porty
Porty dostarczają specyfikację zawierającą informację:
● w jaki sposób zewnętrzna warstwa może używać naszego aplikacji
● w jaki sposób aplikacja może używać zewnętrzną warstwę
Generalnie realizuje się to za pomocą Interfejsów.
Umiejscowienie:
● Porty znajdują się wewnątrz naszej logiki biznesowej.
● Adaptery znajdują się poza logiką biznesową
16. Hexagonal - Adapters
Adaptery muszą być dopasowane do naszej logiki biznesowej i odbywa się to za pomocą Portów do których
się dopasowują.
Zapewniają komunikację warstw UI oraz infrastruktury z warstwą aplikacji
● UI adapter - nakazuje warstwie aplikacji co ma zrobić (przeprowadzić proces biznesowy)
● Infrastructure adapter - aplikacja mówi infrastrukturze co ma zrobić
Powyższe różnice definiują nam co tak naprawdę jest ważne i co za co jest odpowiedzialne.
17. Hexagonal - rodzaje adapterów
● Primary or Driving Adapters
○ owijają się wokół portu (wstrzykiwany)
○ przyjmują przychodzące dane,
transformują i wywołują metodę
udostępnianią przez Port
○ REST/SOAP Controllers, Consumers,
Console Commands
class OrderController
{
public $orderModule;
public function __construct(OrderInterface $orderModule)
{
$this->orderModule = $orderModule;
}
public function checkout()
{
// ....
$this->orderModule->checkout();
// ....
}
}
18. Hexagonal - rodzaje adapterów
● Secondary or Driven Adapters
○ Implementują port
○ Adapter jest wstrzykiwany do wnętrza
naszej aplikacji
○ Mechanizmy persystencji danych,
zewnętrzne API, wyszukiwarki,
utrwalanie w kolejkach, itp.
class OracleOrderDao implements OrderDaoInterface
{
public function get(int $orderId): Order
{
// to do something
}
}
20. Kolejne kroki
● Testy !!!
● CRUD ???
● CQRS
● Onion Architecture
● DDD
UI
Controller
CatalogController.php <- Primary Adapter
Console
Consumer
Application
User
Catalog
Repository
CatalogInterface.php <- Port for Secondary Adapter
CatalogService.php
CatalogInterface.php <- Port for Primary Adapter
Shared
Dto
SomeSharedDto.php
Event
ProductWasCreated.php
Infrastructure
Catalog
MySqlCatalogRepository.php <- Secondary Adapter
RedisCatalogRepository.php <- Secondary Adapter
21. Tips
● Nie podążaj drogą wytyczoną przez dokumentację frameworka, którego używasz.
Dzięki dokumentacji nauczysz się frameworka, poznasz jego możliwości, ale nie
nauczysz się jak zaprojektować dużą aplikację.
● Zrozumienie logiki biznesowej
● Dobranie odpowiedniej architektury (lub paru)
● Separacja
● Testy, testy, testy i jeszcze raz testy
Dotyczy systemu o trudnej do wyodrębnienia i zrozumienia strukturze. Modyfikowanie takiego systemu jest ryzykowne, gdyż nie sposób jest przewidzieć skutków zmian, kod przypomina spaghetti.
A Big Ball of Mud is a software design anti-pattern in which a software system lacks a perceivable structure. This means that, to an outside observer, the system has no discernable architecture, and as such, looks thrown-together, haphazard, and is a massive pain to maintain.
"A Big Ball of Mud is a haphazardly structured, sprawling, sloppy, duct-tape-and-baling-wire, spaghetti-code jungle. These systems show unmistakable signs of unregulated growth, and repeated, expedient repair. Information is shared promiscuously among distant elements of the system, often to the point where nearly all the important information becomes global or duplicated. The overall structure of the system may never have been well defined. If it was, it may have eroded beyond recognition. Programmers with a shred of architectural sensibility shun these quagmires. Only those who are unconcerned about architecture, and, perhaps, are comfortable with the inertia of the day-to-day chore of patching the holes in these failing dikes, are content to work on such systems."
-Brian Foote and Joseph Yoder, 1997
The “big ball of mud”, AKA Spaghetti Architecture, is the anti-pattern for this style, where the packages structure and relations are not explicit, structural cohesion and encapsulation does not exist or is minimal, dependencies follow no rules and it is very difficult to reason about subsystems, to make changes and to refactor. The system is opaque, viscous, fragile and rigid: A Big Ball of Mud!
developowanie - łatwiej i szybciej zacząć projekt od monolitu niż od złożonej architektury mikroserwisowej
Benefits:
Simple to develop: At the beginning of a project it is much easier to go with Monolithic Architecture.
Simple to test: For example, you can implement end-to-end testing by simply launching the application and testing the UI with Selenium.
Simple to deploy: You have to copy the packaged application to a server.
Simple to scale horizontally by running multiple copies behind a load balancer.
Drawbacks:
Maintenance: If Application is too large and complex to understand entirely, it is challenging to make changes fast and correctly.
The size of the application can slow down the start-up time.
You must redeploy the entire application on each update.
Monolithic applications can also be challenging to scale when different modules have conflicting resource requirements.
Reliability: Bug in any module (e.g. memory leak) can potentially bring down the entire process. Moreover, since all instances of the application are identical, that bug impact the availability of the entire application
Regardless of how easy the initial stages may seem, Monolithic applications have difficulty to adopting new and advance technologies. Since changes in languages or frameworks affect an entire application, it requires efforts to thoroughly work with the app details, hence it is costly considering both time and efforts.
Powiedzieć o dobieraniu odpowiedniej technologii pod potrzeby. Nie trzeba się dawać porywać “trendom”. Trzeba przeanalizować wymagania i oszacować co jest sens używać.
Posłużyć się przykładem startupy - nie wiadomo tak do końca w którą stronę będzie się on rozwijał.
Podział ze względu na moduły biznesowe (bounded contexts)
A modular monolith is a system where all of the code powers a single application and there are strictly enforced boundaries between different domains.
Trzeba być konsekwentnym w przestrzeganiu zasad !!!
rozbicie całej aplikacji na moduły/komponenty
zaprojektowanie odpowiedniej struktury
izolacja zależności
zdefiniowanie publicznego interfejsu danego modułu
określenie właścicieli danych
Each component will create a local copy of the data it needs from other components, to be used when needed. When the data changes in the component that owns it, that owner component will trigger a domain event carrying the data changes. The components holding a copy of that data will be listening to that domain event and will update their local copy accordingly.
Shared kernel - Moduł B nasłuchujący zdarzeń z modułu A ma świadomość istnienia A w związku z tym jest częściowo zależny od A, dlatego tworzy się SK od którego zależą A i B jednak nie zależą już od siebie nawzajem. Trzymamy w nim części współdzielone, np definicję eventów. Powinien być jak najmniejszy, aby zmiany w nim nie wymuszały dużych z miał w pozostałych modułach.
Mocno generalizując aplikację możemy podzielić na trzy części:
User interface - jednak nie w rozumieniu klasycznym czyli GUI, które jest tylko jednym z typów UI. Innym przykładem jest UI zapewniający integrację z konsolą
Application core - krótko mówiąc serce naszej aplikacji (logika biznesowa). Używana przez warstwę UI. To jest serce naszej aplikacji, któremu musimy poświęcić najwięcej uwagi.
Infrastructure - zapewnia połączenie naszej aplikacji (Córe) z różnego rodzaju zewnętrznymi mechanizmami np. baza danych, api dostarczanymi przez inne serwisy
Adaptery muszą być dopasowane do naszej logiki biznesowej i odbywa się to za pomocą Portów do których się dopasowują.
Porty należy traktować jako specyfikację w jaki sposób zewnętrzna aplikacja może używać naszego CORE, lub w jaki sposób nasz CORE może używać zewnętrzną aplikację. Generalnie realizuje się to za pomocą Interfejsów.
Naleźy podkreślić fakt, że „porty” czyli Interfejsy muszą się znajdować wewnątrz naszej logiki biznesowej, ponieważ stanowią jej integralną i nierozerwalną część. Natomiast same adaptery znajdują się już poza logiką biznesową. Porty są tworzone po to aby odzwierciedlić to co naprawdę potrzebuje nasza logika biznesowa, nie stanowią prostego API do tworzenia/usuwania obiektów.
UI - tell our application to do something (mówi corowi_ co ma zrobić)
Infrastructure - told by our application to do something (core mówi co zrobić), baza zostaje poinformowana co ma zrobić
Powyższe różnice definiują nam co tak naprawdę jest ważne i co za co odpowiada
This is a very relevant distinction, as it has strong implications on how we build the code that connects those tools with the application core.
The code units that connect the tools to the application core are called adapters (Ports & Adapters Architecture). The adapters are the ones that effectively implement the code that will allow the business logic to communicate with a specific tool and vice-versa.The adapters that tell our application to do something are called Primary or Driving Adapters while the ones that are told by our application to do something are called Secondary or Driven Adapters.
Primary or Driving Adapters
Owijają się wokół portu i mówią naszej „aplikacji” co ma zrobić (Wstrzykiwany)
Powiązane z warstwą UI, potrafią przyjąć przychodzące dane, dokonać ich transformacji na postać zrozumiałą przez aplikację (określoną w specyfikacji Portu) i wywoałać metodę udostępnianią przez Port
REST/SOAP Controllers, Consumers, Console Commands
DODAĆ PRZYKŁAD Z KODEM KONTROLERA I AKCJI TODO
Secondary or Driven Adapters
Implementują Port (Implementowany)
Następnie taki adapter jest wstrzykiwany do wnętrza naszej „aplikacji” (tam gdzie oczywiście jest potrzebny, czyli wymagany przez port)
Mechanizmy persystencji danych, zewnętrzne API, wyszukiwarki, utrwalanie w kolejkach, itp.
DODAĆ PRZYKŁAD
Adaptery są zależne od konkretnych technologii (MySQL, HTTP) oraz od konkretnych portów poprzez interfejsy. Jednak nasza logika biznesowa pozostaje zależna tylko i wyłącznie od portów, które są dostosowane do potrzeb logiki biznesowej.
Primary or Driving Adapters
Owijają się wokół portu i mówią naszej „aplikacji” co ma zrobić (Wstrzykiwany)
Powiązane z warstwą UI, potrafią przyjąć przychodzące dane, dokonać ich transformacji na postać zrozumiałą przez aplikację (określoną w specyfikacji Portu) i wywoałać metodę udostępnianią przez Port
REST/SOAP Controllers, Consumers, Console Commands
DODAĆ PRZYKŁAD Z KODEM KONTROLERA I AKCJI TODO
Secondary or Driven Adapters
Implementują Port (Implementowany)
Następnie taki adapter jest wstrzykiwany do wnętrza naszej „aplikacji” (tam gdzie oczywiście jest potrzebny, czyli wymagany przez port)
Mechanizmy persystencji danych, zewnętrzne API, wyszukiwarki, utrwalanie w kolejkach, itp.
DODAĆ PRZYKŁAD
Adaptery są zależne od konkretnych technologii (MySQL, HTTP) oraz od konkretnych portów poprzez interfejsy. Jednak nasza logika biznesowa pozostaje zależna tylko i wyłącznie od portów, które są dostosowane do potrzeb logiki biznesowej.
CQRS
jeszcze większa separacja od frameworka
odizolujemy logikę odpowiedzialną za obsługę przypadków użycia (use case) od warstwy prezentacji UI, przez co staje się przenoszalna i niezależna (można ją używać zarówno przez konsolę jak i gui)
Onion Architecture
DDD
Oczywiście podziękować.
Jak będzie feedback i wyrazicie zainteresowanie prezentacja jak refaktoryzować zastany monolit do Monolitu 2.0 na fb i kanale shit.it to przygotuję prezentację dedytkowaną w tym temacie.