Wdrażanie CQRS w Go: Praktyczny przewodnik po skalowalnej architekturze
Tworzenie CQRS w Go bez zbędnych formalności
CQRS to jeden z tych wzorców, który jest często przesadnie promowany, nadmiernie skomplikowany i niekiedy błędnie diagnozowany jako lekarstwo na nudny, zwykły CRUD.
Przydatna wersja tego wzorca jest znacznie prostsza: oddziel kod zmieniający stan od kodu odczytującego stan, a następnie pozwól każdej ze stron ewoluować zgodnie z jej własnym zadaniem. Martin Fowler opisuje CQRS jako użycie innego modelu do aktualizowania informacji niż ten używany do jej odczytu, jednocześnie ostrzegając, że dla większości systemów dodaje to ryzykowną złożoność. Microsoft stawia ten sam fundamentalny punkt w bardziej operacyjnych terminach: oddziel modele odczytu i zapisu, aby każdy z nich mógł być optymalizowany niezależnie.

Jeśli pracujesz w Go, ten pomysł mapuje się na ten język w sposób niezwykły. Go jest dobry w wyznaczaniu wyraźnych granic, małych interfejsach, nudnych typach danych i pakietach zorientowanych przypadkach użycia. Dzięki temu podstawowe CQRS w Go jest znacznie mniej teatralne, niż często wygląda na slajdach konferencyjnych. Nie potrzebujesz event sourcingu, Kafki ani trzech baz danych, aby zacząć. W rzeczywistości zarówno wytyczne Microsoftu dotyczące CQRS, jak i przykłady Go z Three Dots Labs pokazują, że prosta implementacja może dzielić to samo podstawowe magazynowanie, z osobnymi handlerami komend i zapytań dodanymi na początku, a bardziej zaawansowaną infrastrukturą wprowadzaną dopiero wtedy, gdy problem faktycznie tego wymaga.
Co CQRS tak naprawdę oznacza
W centrum uwagi CQRS rysuje wyraźną linię między komendami a zapytaniami. Zapytanie odczytuje dane i nie powinno modyfikować stanu systemu. Komenda zmienia stan i nie powinna zwracać danych domenowych jako swojego głównego rezultatu. Three Dots Labs formułują to w praktycznych terminach Go: zapytania zwracają dane, a komendy wprowadzają zmiany, przy czym błędy są normalnym wynikiem komendy. To podstawowy manewr. Wszystko inne jest opcjonalne.
Powszechnym nieporozumieniem jest to, że CQRS automatycznie oznacza osobne bazy danych, asynchroniczne projekcje lub event sourcing. To nieprawda. Przewodnik po wzorcach Microsoftu wyraźnie traktuje osobne magazyny danych jako bardziej zaawansowaną formę, a nie domyślną, a Three Dots Labs pokazują implementację w Go, w której zapytania odczytują z tej samej bazy danych co zapisy, ponieważ jest to wystarczające dla danego systemu. Jeśli Twój artykuł ma nauczyć tylko jednej rzeczy wyraźnie, niech będzie to następująca: CQRS jest przede wszystkim wyborem modelowania i struktury aplikacji, a nie wymaganym pakietem rozwiązań dla systemów rozproszonych.
Innym ważnym szczegółem jest nazywanie. Komendy powinny modelować intencje biznesowe, a nie mutacje magazynowania. Przykład Microsoftu kontrastuje „Zarezerwuj pokój hotelowy” z „Ustaw ReservationStatus na Zarezerwowany”, a Three Dots Labs zalecają nazwy zbliżone do tego, jak mówią eksper domenowi, takie jak „ScheduleTraining” (ZaplanujSzkolenie) lub „CancelTraining” (AnulujSzkolenie), zamiast ogólnych czasowników „Create” (Utwórz) i „Delete” (Usuń). W Go ta dyscyplina nazywania się opłaca, ponieważ nazwy komend często stają się nazwami typów, nazwami handlerów i granicami pakietów.
Dlaczego zespoły sięgają po CQRS
CQRS staje się atrakcyjne, gdy pojedynczy model CRUD zaczyna źle pełnić zbyt wiele ról. Wytyczne Microsoftu wymieniają typowe punkty presji: reprezentacje odczytu i zapisu tych samych danych się różnią, jednoczesne aktualizacje tworzą konkurencję o blokady, wydajność odczytu cierpi pod wpływem złożoności zapytań, a wspólne encje zamieniają zasady bezpieczeństwa w gmatwaninę. Innymi słowy, problemem nie jest to, że CRUD jest moralnie zły. Problemem jest to, że jeden model jest zmuszany do zaspokojenia niezgodnych ze sobą wymagań jednocześnie.
To jest szczególnie powszechne w produktach technicznych. Zapisy zazwyczaj dbają o walidację, inwarianty, transakcje i zasady biznesowe. Odczyty zazwyczaj dbają o filtry, łączenia (joins), agregację, cacheowanie, sortowanie i serwowanie dokładnie tej struktury, której potrzebuje strona lub API. CQRS pozwala stronie zapisu pozostać rygorystyczną i zorientowaną na domenę, podczas gdy strona odczytu pozostaje pragmatyczna i zorientowana na DTO. Microsoft wyraźnie zaleca model zapisu skupiony na walidacji i spójności oraz model odczytu skupiony na DTO lub projekcjach zoptymalizowanych pod kątem prezentacji i responsywności.
Istnieje również korzyść na poziomie zespołu. Three Dots Labs argumentują, że rozdzielenie komend i zapytań poprawia rozluźnienie (decoupling), sprawia, że przepływ wykonania jest clearer (jasniejszy), i przyspiesza onboarding, ponieważ deweloperzy mogą przeglądać małą listę dostępnych komend i zapytań, zamiast gonić logikę przez losowe warstwy usług. Microsoft podobnie zauważa, że CQRS jest szczególnie przydatne w środowiskach kolaboracyjnych, gdzie wielu użytkowników aktualizuje te same dane i komendy potrzebują wystarczającej granularności, aby zapobiegać lub rozwiązywać konflikty.
Moje nieco opiniotwórcze zdanie brzmi tak: większość zespołów przybiera CQRS zbyt późno, po tym jak jedna „usługa” już zamieniła się w monolit o miękkim rdzeniu. Ale wiele zespołów przybiera go też zbyt wcześnie, głównie dlatego, że diagram architektury wyglądał drogo i dlatego poważnie. Prawidłowy moment to ten, gdy odczyty i zapisy wyraźnie oddalają się od siebie pod względem kształtu, szybkości lub zasad, a nie wtedy, gdy Twoja aplikacja „todo” ma ambicje.
Korzyści i koszt
Podstawowe CQRS ma realne korzyści nawet przed dodaniem jakiejkolwiek komunikacji (messaging) lub osobnych magazynów. Daje Ci mniejsze modele komend, mniejsze modele zapytań, clearer (jasniejsze) przypadki użycia i bardziej oczywiste miejsca do stosowania przekrojowych zagadnień, takich jak logowanie i instrumentacja. Three Dots Labs wyraźnie wskazują na lepszą organizację kodu, rozluźnienie i prostsze modele jako natychmiastowe sukcesy, podczas gdy Microservices.io podkreśla prostsze modele komend i zapytań oraz wsparcie dla zdewalutowanych, skalowalnych widoków odczytu.
Gdy problem to uzasadnia, CQRS otwiera również drogę do silniejszej optymalizacji strony odczytu. Wytyczne Microsoftu wskazują, że osobne modele odczytu mogą używać DTO, projekcji, replik tylko do odczytu lub nawet zupełnie innej technologii magazynowania. Wskazuje również na widoki materializowane jako sposób na unikanie ciężkich joinów i ścieżek zapytań obciążonych ORM. Jeśli oceniasz, której warstwy dostępu do danych użyć po stronie zapisu, Porównanie ORM w Go dla PostgreSQL omawia kompromisy między GORM, Ent, Bun i sqlc w praktycznych terminach. To tam CQRS zaczyna się opłacać operacyjnie, nie tylko strukturalnie.
Koszt jest również realny. Ostrzeżenie Fowlera nadal jest właściwym punktem wyjścia: dla większości systemów CQRS dodaje ryzykowną złożoność. Microsoft wymienia zwiększoną złożoność i ewentualną spójność jako kluczowe rozważania, podczas gdy Microservices.io dodaje potencjalną duplikację kodu i opóźnienia replikacji w widokach odczytu. Jeśli podzielisz magazyny, dziedziczysz również zadanie utrzymania ich synchronizacji, zwykle przez zdarzenia (events), bez polegania na porządkowej transakcji rozproszonej między Twoją bazą danych a brokerem.
Event sourcing nie usuwa tego kosztu; zmienia jego kształt. Wytyczne Microsoftu dotyczące CQRS mówią, że event sourcing może sprawić, że magazyn zdarzeń stanie się jedynym źródłem prawdy i pozwoli na odbudowę widoków materializowanych przez odtworzenie historii, podczas gdy Event Horizon wskazuje na śledzenie i logowanie audytowe jako główne korzyści. Ale Microsoft również ostrzega, że generowanie widoków, odtwarzanie i obsługa zdarzeń dodaje więcej złożoności projektowej i sugeruje migawki (snapshots), aby zmniejszyć koszty odtwarzania. Dlatego wolę wyjaśniać event sourcing jako „CQRS plus drugą trudną decyzję”, a nie jako bilet wejściowy.
Przydatną zasadą, którą warto mieć na uwadze, jest to, że podstawowe CQRS jest tanie, podczas gdy rozproszone CQRS jest drogie, a łączenie tych dwóch tematów w jedną całość to jeden z najczęstszych sposobów, w jaki zespoły kończą z znacznie większą złożonością, niż problem kiedykolwiek wymagał.
Prosta implementacja CQRS w Go
Zmysłowym pierwszym krokiem w Go jest utrzymanie jednej bazy danych i podział tylko warstwy aplikacji. Komendy posiadają zasady biznesowe i persistencję. Zapytania zwracają modele odczytu ukształtowane dla wywołujących. To dokładnie ten rodzaj podstawowego CQRS, który Three Dots Labs zalecają przed sięgnięciem po asynchroniczne szyny (buses) lub osobne magazyny odczytu.
Zacznij od komend
package blog
import (
"context"
"errors"
"time"
)
type PublishPostCommand struct {
Title string
Slug string
BodyMD string
Author string
}
type PostRepository interface {
NextID(ctx context.Context) (string, error)
Save(ctx context.Context, post Post) error
}
type Post struct {
ID string
Title string
Slug string
BodyMD string
Author string
PublishedAt time.Time
}
type PublishPostHandler struct {
Repo PostRepository
Now func() time.Time
}
func (h PublishPostHandler) Handle(ctx context.Context, cmd PublishPostCommand) error {
if cmd.Title == "" || cmd.Slug == "" || cmd.BodyMD == "" {
return errors.New("title, slug, and body are required")
}
id, err := h.Repo.NextID(ctx)
if err != nil {
return err
}
post := Post{
ID: id,
Title: cmd.Title,
Slug: cmd.Slug,
BodyMD: cmd.BodyMD,
Author: cmd.Author,
PublishedAt: h.Now(),
}
return h.Repo.Save(ctx, post)
}
Ten handler nie próbuje serwować strony, kształtować odpowiedzi listy ani optymalizować SQL dla siatki kart. Po prostu wymusza intencję i utrwalania ważny agregat. To strona komendy wykonująca jedno zadanie dobrze.
Dodaj zapytania
package blog
import "context"
type PostView struct {
ID string
Title string
Slug string
Author string
PublishedAt string
Excerpt string
}
type LatestPostsQuery struct {
Limit int
}
type PostReadModel interface {
Latest(ctx context.Context, limit int) ([]PostView, error)
BySlug(ctx context.Context, slug string) (PostView, error)
}
type LatestPostsHandler struct {
ReadModel PostReadModel
}
func (h LatestPostsHandler) Handle(ctx context.Context, q LatestPostsQuery) ([]PostView, error) {
limit := q.Limit
if limit <= 0 {
limit = 10
}
return h.ReadModel.Latest(ctx, limit)
}
type GetPostBySlugQuery struct {
Slug string
}
type GetPostBySlugHandler struct {
ReadModel PostReadModel
}
func (h GetPostBySlugHandler) Handle(ctx context.Context, q GetPostBySlugQuery) (PostView, error) {
return h.ReadModel.BySlug(ctx, q.Slug)
}
Zauważ, że strona odczytu zwraca PostView, a nie model zapisu. To odzwierciedla zalecenie Microsoftu, aby model odczytu był zoptymalizowany pod kątem DTO i prezentacji, podczas gdy model zapisu jest dostosowany do integralności transakcyjnej i zasad domenowych.
Podłącz to jak aplikację Go, a nie świątynię
package app
import "your/module/internal/blog"
type Application struct {
Commands Commands
Queries Queries
}
type Commands struct {
PublishPost blog.PublishPostHandler
}
type Queries struct {
LatestPosts blog.LatestPostsHandler
GetPostBySlug blog.GetPostBySlugHandler
}
Ten kształt nie jest przypadkowy. Three Dots Labs używają bardzo podobnego wzorca w Wild Workouts: typ Application eksponujący Commands i Queries, z konkretnymi handlerami podłączonymi z osobnych pakietów app/command i app/query. Ich kod kompozycji usług importuje te pakiety osobno i konstruuje z nich pojedynczy obiekt aplikacji. To czysty, „go-ish” sposób na uczynienie granicy oczywistą bez „Framework Drama” (Dramy Frameworkowej). Jeśli Twój graf zależności staje się złożony wraz z mnożeniem się handlerów, Wstrzykiwanie zależności w Go omawia Wire, Dig i wzorce wstrzykiwania przez konstruktor, które naturalnie komponują się z tą opartą na handlerach strukturą.
Jeśli później potrzebujesz asynchronicznych komend, zdarzeń międzyusługowych lub zdewalutowanego indeksu wyszukiwania, możesz je dodać z tej podstawy. Three Dots Labs wyraźnie przedstawiają asynchroniczne szyny komend i osobne bazy danych zapytań jako późniejsze optymalizacje, a nie punkt startowy.
Warto znać biblioteki Go
Ekosystem CQRS w Go jest węższy niż ten w .NET, co szczero mówiąc, jest błogosławieństwem. Możesz zmapować prawdziwe opcje w ciągu popołudnia i uniknąć przyjęcia trzech abstrakcji, których nie potrzebujesz.
Watermill
Watermill to najbardziej wyraźny, nowoczesny wybór, gdy chcesz CQRS plus komunikacji (messaging). Jego komponent CQRS to wysokopoziomowe API, które pozwala pracować ze strukturami Go zamiast surowymi wiadomościami, a jego bloki budowlane obejmują EventBus, EventProcessor, CommandBus i CommandProcessor. Dokumentacja obejmuje również grupy handlerów zdarzeń dla uporządkowanego przetwarzania na wspólnych tematach, przykład modelu odczytu i niestandardowe metadane serializacji. Poza warstwą CQRS, Watermill obsługuje szeroki zakres backendów pub/sub, w tym RabbitMQ, Kafka, NATS Jetstream, Redis Streams, Google Cloud Pub/Sub, SQL, HTTP i inne. Pkg.go.dev oznacza Watermill jako gotowy do produkcji ze stabilnym publicznym API od wersji v1.0.0, a aktualna opublikowana wersja modułu to v1.5.2, z GitHubem wyświetlającym to wydanie z 13 maja.
commandBus, err := cqrs.NewCommandBusWithConfig(pub, cfg)
eventBus, err := cqrs.NewEventBusWithConfig(pub, cfg)
commandProcessor, err := cqrs.NewCommandProcessorWithConfig(router, cfg)
eventProcessor, err := cqrs.NewEventProcessorWithConfig(router, cfg)
Używaj Watermill, gdy komendy i zdarzenia muszą przekraczać granice procesów, gdy chcesz, aby semantyka ponownych prób i dostaw była pierwszorzędna, lub gdy wiesz, że Twoja „prosta” usługa jest już w połowie drogi do rzeczywistości napędzanej zdarzeniami. Wadą jest to, że teraz prowadzisz rozmowy o brokerze, tematach, kolejności i idempotentności niezależnie od tego, czy tego chcesz, czy nie. To nie jest wada Watermill. To koszt przestrzeni problemów.
Event Horizon
Event Horizon to zestaw narzędzi CQRS i event sourcingu dla Go. Jego twórcy opisują go jako używany w systemach produkcyjnych, ale również wskazują, że API nie jest ostateczne. Zestaw narzędzi udostępnia pomocniki rejestracji agregatów, komend i zdarzeń, oficjalne implementacje magazynu zdarzeń dla wariantów pamięci i MongoDB, wsparcie dla projekcji i repozytoriów oraz przykłady, w tym aplikacja oparta na wzorcu outbox. Strumień wydań nadal jest aktywny, z GitHubem pokazującym v0.17.0 z 16 czerwca i wcześniejszymi wydaniem dodającymi funkcje takie jak migawki, projekcje z możliwością ponownej próby, trwałym planowaniem komend i wzorcem outbox.
eh.RegisterAggregate(func(id uuid.UUID) eh.Aggregate {
return &InvoiceAggregate{ID: id}
})
eh.RegisterCommand(func() eh.Command {
return &CreateInvoiceCommand{}
})
Event Horizon ma najwięcej sensu, gdy event sourcing jest celem, a nie opcjonalną przyszłą rozszerzalnością. Jeśli chcesz strumienie przyjazne audytowi, odtwarzaną historię, projekcje i model skoncentrowany na magazynie zdarzeń, jest to poważna opcja. Jeśli chcesz tylko czystsze usługi aplikacji w monolicie, jest to prawdopodobnie więcej mechanizmów, niż potrzebujesz. Uwaga „API nie jest ostateczne” oznacza również, że powinieneś założyć nieco więcej adaptacji w czasie niż w przypadku Watermill.
Go-MediatR
Go-MediatR nie jest pełnym frameworkiem CQRS, ale jest przydatny dla CQRS wewnątrz procesu. Jego README opisuje go jako implementację wzorca mediatora używaną z CQRS, z dystrybucją żądań/odpowiedzi dla komend i zapytań, dystrybucją powiadomień dla zdarzeń i zachowaniami pipeline dla przekrojowych zagadnień. Projekt ma również oznaczone wydania, z GitHubem wyświetlającym v1.4.0 jako najnowsze wydanie i wskazującym na bezpieczne dla wątków rejestrowanie handlerów i ulepszenia związane z konkurencją.
resp, err := mediatr.Send[*CreateProductCommand, *CreateProductResponse](ctx, cmd)
post, err := mediatr.Send[*GetPostBySlugQuery, *PostView](ctx, query)
To dobre dopasowanie, jeśli chcesz komend i zapytań opartych na handlerach, ale nie brokera, silnika projekcji ani magazynu zdarzeń. Jest szczególnie przyjazny dla zespołów przychodzących z MediatR w .NET. Kompromis jest równie wyraźny: nadal musisz zaprojektować własną persistencję, strategię odświeżania modelu odczytu i historię integracji poza procesem. Innymi słowy, daje Ci granicę aplikacji, nie całą architekturę.
Starsze frameworki i materiały referencyjne
Istnieje starsze biblioteki CQRS w Go, które nadal są pouczające, ale traktowałbym je jako materiały referencyjne, zanim traktowałbym je jako domyślne dla nowych projektów (greenfield).
jetbasrawi/go.cqrs opisuje się jako referencyjną implementację CQRS w Go z aplikacjami przykładowymi opartymi na zasadach Grega Younga. Jednak pkg.go.dev pokazuje brak ważnego go.mod, brak oznaczonej wersji i brak stabilnej wersji, podczas gdy GitHub pokazuje brak wydań i metadane pakietu zostały opublikowane 7,4 roku temu. To przydatna historia, nie silny sygnał dla świeżej adopcji produkcyjnej w 2026 roku.
andrewwebber/cqrs jest podobny: dostarcza event sourcing, wydawanie i przetwarzanie komend, publikowanie zdarzeń i generowanie modeli odczytu z opublikowanych zdarzeń, ale metadane pakietu zostały również opublikowane 7,4 roku temu. Na pewno to przeczytam, jeśli chcesz zrozumieć, jak wcześniejsze biblioteki CQRS w Go podchodziły do problemu. Będę ostrożny w uczynieniu tego fundamentem nowej bazy kodu, chyba że jesteś szczęśliwy, stając się półetatowym twórcą własnej stosu architektonicznego.
Praktyczny układ projektu Go
Typowy układ CQRS w Go powinien czynić przypadki użycia oczywistymi, a nie chować je pod ogólnymi abstrakcjami. Wild Workouts to dobre odniesienie tutaj. Repozytorium oddziela ograniczone konteksty pod internal, utrzymuje komendy i zapytania w odrębnych pakietach aplikacji i podłącza je do typu Application eksponującego Commands i Queries. Kompozycja usług łączy adaptery, handlery i zależności w sposób jawny. Wzorce opisane tutaj są zgodne z szerszymi wytycznymi w Struktura Projektu Go: Praktyki i Wzorce, która omawia szerszy zestaw decyzji dotyczących układu, przed którymi stają zespoły wraz ze wzrostem baz kodu Go.
Pragmatyczny układ wygląda następująco:
internal/
blog/
app/
app.go
command/
publish_post.go
unpublish_post.go
query/
get_post_by_slug.go
latest_posts.go
domain/
post.go
slug.go
adapters/
postgres/
post_repository.go
post_read_model.go
ports/
http/
handler.go
service/
application.go
Ten układ ma kilka zalet.
Po pierwsze, handlery komend i zapytań mieszkają blisko przypadków użycia, które implementują. To sprawia, że trudniej ukryć zachowanie biznesowe w repozytoriach lub handlerach nazwanych na warstwy transportowe. Three Dots Labs robią to bezpośrednio w Wild Workouts, gdzie app/command i app/query są osobnymi pakietami, a najwyższego poziomu Application grupuje handlery według odpowiedzialności.
Po drugie, pakiet domenowy może pozostać skupiony na inwariantach i zachowaniu, podczas gdy strona zapytań jest wolna w zwracaniu DTO i projekcji. To zgadza się z wytycznymi Microsoftu dotyczącymi modeli zapisu i odczytu i unika wspólnego antywzorca CQRS, w którym strona zapytań jest zmuszana do powrotu przez obiekty domenowe tylko dla ideologicznej czystości.
Po trzecie, ta struktura skaluje się od najmniejszego przydatnego CQRS do cięższych wariantów. Możesz utrzymać jedną bazę danych PostgreSQL i dwie implementacje repozytorium dzisiaj, a następnie dodać indeks wyszukiwania lub asynchroniczną projekcję odczytu później, bez konieczności przepisywania całego kształtu aplikacji. Three Dots Labs wyraźnie opisują tę progresję od podstawowego CQRS do asynchronicznych szyn komend i osobnych magazynów zapytań tylko wtedy, gdy system ich potrzebuje.
Kiedy CQRS pasuje, a kiedy nie
CQRS ma sens, gdy odczyty i zapisy są prawdziwie różnymi problemami. Microsoft zaleca go dla obciążeń, w których modele odczytu i zapisu wymagają niezależnej optymalizacji, gdzie wielu użytkowników współpracuje nad tymi samymi danymi i gdzie jasne oddzielenie pomaga w wydajności, skalowalności i bezpieczeństwie. Microservices.io dodaje kolejny klasyczny dopasowanie: zdewalutowane, wysokowydajne widoki budowane ze zdarzeń domenowych lub materializowanych projekcji. Three Dots Labs wskazują również na złożoną logikę biznesową, utrzymalność i przyszłe rozszerzenie w kierunku asynchronicznych komend lub specjalizowanych magazynów odczytu jako silne powody do przyjęcia go w Go.
W praktyce oznacza to często systemy z bogatymi zasadami domenowymi, kosztownymi modelami odczytu, widokami raportowymi, które nie mapują się neatly (gładko) na agregaty, lub mikrousługi, które publikują zdarzenia i budują projekcje gdzie indziej. W tych kontekstach Wzorzec Saga dla transakcji rozproszonych często pojawia się obok CQRS jako mechanizm koordynacji wieloetapowych operacji biznesowych, które obejmują granice usług. Pasuje również do produktów, w których strona zapisu musi być rygorystyczna i audytowalna, podczas gdy strona odczytu musi być szybka i ukształtowana pod kątem konsumpcji przez UI lub API. Jeśli już rozmawiasz o projekcjach, replikach lub odbudowie widoków ze zdarzeń, prawdopodobnie jesteś w terytorium CQRS, niezależnie od tego, czy używasz tej etykiety, czy nie.
CQRS nie ma sensu, gdy Twoja usługa jest prostym edytorem danych. Fowler mówi wprost, że dla większości systemów CQRS dodaje ryzykowną złożoność, a Three Dots Labs mówią, że proste usługi CRUD, które otrzymują i zwracają zasadniczo te same dane, nie są dobrym dopasowaniem. W ich własnym przykładzie Wild Workouts, prostsza usługa użytkowników nie używa Czystej Architektury i CQRS, ponieważ wzorce nie spłaciłyby się tam.
To jest część warta powiedzenia jasno w technicznym blogu: CQRS to nie odznaka dojrzałości, ale celowy kompromis, i ma sens tylko wtedy, gdy faktycznie potrzebujesz tego, co Ci daje. Jeśli Twój panel administracyjny zapisuje wiersze i odczytuje te same wiersze z powrotem, nie oddzielaj modelu tylko dlatego, że możesz. Jeśli Twoje handlery komend to głównie „ustaw pole X na rekordzie Y”, nie masz problemu CQRS. Masz normalną aplikację, a to jest w pełni szanowany software.
Zamykające myśli
Najlepszym sposobem implementacji CQRS w Go jest rozpoczęcie od nudnej wersji. Oddziel handlery komend od handlerów zapytań. Pozwól komendom modelować intencje biznesowe. Pozwól zapytaniom zwracać modele odczytu. Utrzymaj tę samą bazę danych, jeśli to wszystko, czego potrzebujesz. Następnie, tylko wtedy, gdy system wymusi Ci rękę, dodaj asynchroniczne szyny, projekcje, osobne magazyny lub event sourcing. Ta progresja jest zgodna z ostrzeżeniem Fowlera dotyczącym złożoności, stopniowanymi wytycznymi Microsoftu dotyczącymi CQRS i pragmatycznymi przykładami Go z Three Dots Labs.
Jeśli potrzebujesz biblioteki, Watermill to najsilniejszy wybór ogólnego celu dla CQRS napędzanego wiadomościami w Go, Event Horizon jest przekonujący, gdy event sourcing jest środkiem ciężkości, a Go-MediatR to dobry lekki dotyk, gdy potrzebujesz tylko dystrybucji komend i zapytań wewnątrz procesu. Wszystko inne powinno bardzo ostrożnie zyskiwać swoje miejsce. Dla szerszej mapy struktury kodu, integracji i wzorców dostępu do danych w produkcyjnych systemach Go, Przewodnik po Architektury Aplikacji jest przydatnym towarzyszem.
To, w ostateczności, jest najbardziej „go-like” odpowiedzią na CQRS: używaj wzorca, nie kostiumu.