[{"data":1,"prerenderedAt":732},["ShallowReactive",2],{"post-szesc-senioralnych-pytan-o-architekture":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"author":11,"tags":12,"readingTime":18,"body":19,"_type":726,"_id":727,"_source":728,"_file":729,"_stem":730,"_extension":731},"\u002Fblog\u002Fszesc-senioralnych-pytan-o-architekture","blog",false,"","Sześć senioralnych pytań o architekturę — liczy się cena, nie definicja","Dual-write, idempotencja, eventual consistency, granice kontekstów, CQRS, transakcje rozproszone. Sześć pytań z rozmowy, które oddzielają seniora od mida — bo sprawdzają nie znajomość terminu, lecz świadomość kompromisu.","2026-07-07","Kamil Sułek",[13,14,15,16,17],"architektura","EDA","mikroserwisy","systemy rozproszone","rozmowa senioralna",9,{"type":20,"children":21,"toc":717},"root",[22,52,57,64,74,86,107,134,191,210,231,237,246,273,339,414,420,429,441,460,466,475,494,543,548,554,563,575,608,614,623,642,647,670,689,695,706,711],{"type":23,"tag":24,"props":25,"children":26},"element","p",{},[27,30,36,38,43,45,50],{"type":28,"value":29},"text","Te sześć pytań wraca na rozmowach senioralnych. Nie sprawdzają, czy ",{"type":23,"tag":31,"props":32,"children":33},"strong",{},[34],{"type":28,"value":35},"znasz",{"type":28,"value":37}," słowo\n„outbox\" albo „saga\" — tego można się nauczyć na pamięć. Sprawdzają, czy widzisz ",{"type":23,"tag":31,"props":39,"children":40},{},[41],{"type":28,"value":42},"cenę",{"type":28,"value":44},":\nkażdy z tych wzorców kupuje jakąś własność systemu rozproszonego (niezawodność, luźne\nspięcie, skalę) i płaci za nią czymś konkretnym — opóźnieniem, złożonością, ostateczną\nspójnością, kosztem operacyjnym. Mid nazwie wzorzec. Senior nazwie cenę, zanim go o nią\nzapytają — i wie, kiedy po wzorzec ",{"type":23,"tag":31,"props":46,"children":47},{},[48],{"type":28,"value":49},"nie",{"type":28,"value":51}," sięgać.",{"type":23,"tag":24,"props":53,"children":54},{},[55],{"type":28,"value":56},"Poniżej moja odpowiedź na każde z nich. Pytania 1, 2 i 6 różnicują najmocniej, bo to\nw praktyce ten sam problem oglądany z trzech stron.",{"type":23,"tag":58,"props":59,"children":61},"h2",{"id":60},"_1-dual-write-baza-commituje-publish-pada",[62],{"type":28,"value":63},"1. Dual-write: baza commituje, publish pada",{"type":23,"tag":24,"props":65,"children":66},{},[67,72],{"type":23,"tag":31,"props":68,"children":69},{},[70],{"type":28,"value":71},"Pytanie:",{"type":28,"value":73}," zapisujesz encję do bazy i publikujesz event na kolejkę. Baza commituje,\npublikacja pada. Co się dzieje i jak to naprawiasz?",{"type":23,"tag":24,"props":75,"children":76},{},[77,79,84],{"type":28,"value":78},"To są ",{"type":23,"tag":31,"props":80,"children":81},{},[82],{"type":28,"value":83},"dwa zapisy do dwóch systemów bez wspólnej transakcji",{"type":28,"value":85}," — i to jest cała pułapka.\nBaza się zacommitowała, broker nie dostał eventu → masz dane, o których reszta systemu\nnigdy się nie dowie. Albo odwrotnie: opublikujesz najpierw, transakcja się cofnie →\nevent o danych, których nie ma. Oba przypadki to cicha korozja spójności na poziomie\ncałego systemu, nie jednej usługi.",{"type":23,"tag":24,"props":87,"children":88},{},[89,91,98,100,105],{"type":28,"value":90},"Owinięcie ",{"type":23,"tag":92,"props":93,"children":95},"code",{"className":94},[],[96],{"type":28,"value":97},"publish",{"type":28,"value":99}," w retry nie ratuje — proces może paść ",{"type":23,"tag":31,"props":101,"children":102},{},[103],{"type":28,"value":104},"między",{"type":28,"value":106}," commitem a publikacją.\nDopóki są to dwa zapisy do dwóch systemów, zawsze istnieje okno, w którym jeden się udał, a drugi nie.",{"type":23,"tag":24,"props":108,"children":109},{},[110,112,117,119,124,126,132],{"type":28,"value":111},"Rozwiązanie: nie rób dwóch ",{"type":23,"tag":31,"props":113,"children":114},{},[115],{"type":28,"value":116},"commitów",{"type":28,"value":118},". Zrób jeden — oba zapisy w ",{"type":23,"tag":31,"props":120,"children":121},{},[122],{"type":28,"value":123},"tej samej\ntransakcji",{"type":28,"value":125},": obok encji wstaw wiersz do tabeli ",{"type":23,"tag":92,"props":127,"children":129},{"className":128},[],[130],{"type":28,"value":131},"outbox",{"type":28,"value":133},":",{"type":23,"tag":135,"props":136,"children":140},"pre",{"className":137,"code":138,"language":139,"meta":7,"style":7},"language-sql shiki shiki-themes github-dark","BEGIN;\n  INSERT INTO orders (...) VALUES (...);\n  INSERT INTO outbox (id, type, payload, created_at)\n    VALUES (gen_random_uuid(), 'OrderPlaced', '{...}', now());\nCOMMIT;\n","sql",[141],{"type":23,"tag":92,"props":142,"children":143},{"__ignoreMap":7},[144,155,164,173,182],{"type":23,"tag":145,"props":146,"children":149},"span",{"class":147,"line":148},"line",1,[150],{"type":23,"tag":145,"props":151,"children":152},{},[153],{"type":28,"value":154},"BEGIN;\n",{"type":23,"tag":145,"props":156,"children":158},{"class":147,"line":157},2,[159],{"type":23,"tag":145,"props":160,"children":161},{},[162],{"type":28,"value":163},"  INSERT INTO orders (...) VALUES (...);\n",{"type":23,"tag":145,"props":165,"children":167},{"class":147,"line":166},3,[168],{"type":23,"tag":145,"props":169,"children":170},{},[171],{"type":28,"value":172},"  INSERT INTO outbox (id, type, payload, created_at)\n",{"type":23,"tag":145,"props":174,"children":176},{"class":147,"line":175},4,[177],{"type":23,"tag":145,"props":178,"children":179},{},[180],{"type":28,"value":181},"    VALUES (gen_random_uuid(), 'OrderPlaced', '{...}', now());\n",{"type":23,"tag":145,"props":183,"children":185},{"class":147,"line":184},5,[186],{"type":23,"tag":145,"props":187,"children":188},{},[189],{"type":28,"value":190},"COMMIT;\n",{"type":23,"tag":24,"props":192,"children":193},{},[194,196,201,203,208],{"type":28,"value":195},"Osobny proces czyta ",{"type":23,"tag":92,"props":197,"children":199},{"className":198},[],[200],{"type":28,"value":131},{"type":28,"value":202}," — odpytując tabelę w pętli albo śledząc log transakcyjny\nbazy — publikuje na broker i oznacza wiersz jako wysłany. Teraz jest atomowo: commit →\nevent trwale zakolejkowany w outboxie; rollback → nie ma ani encji, ani eventu. Publikacja\nstaje się ",{"type":23,"tag":31,"props":204,"children":205},{},[206],{"type":28,"value":207},"at-least-once",{"type":28,"value":209}," (ten proces po restarcie może wysłać event dwa razy) — i właśnie\ndlatego istnieje pytanie 2.",{"type":23,"tag":24,"props":211,"children":212},{},[213,215,222,224,229],{"type":28,"value":214},"To jest lustrzane odbicie zasady z ",{"type":23,"tag":216,"props":217,"children":219},"a",{"href":218},"\u002Fblog\u002Fddd-eda-w-systemie-iot",[220],{"type":28,"value":221},"poprzedniego wpisu",{"type":28,"value":223},"\n„decyzja wyprzedza persystencję\": tutaj ",{"type":23,"tag":31,"props":225,"children":226},{},[227],{"type":28,"value":228},"event nie może wyprzedzić commitu",{"type":28,"value":230},".",{"type":23,"tag":58,"props":232,"children":234},{"id":233},"_2-idempotencja-ten-sam-event-dwa-razy",[235],{"type":28,"value":236},"2. Idempotencja: ten sam event dwa razy",{"type":23,"tag":24,"props":238,"children":239},{},[240,244],{"type":23,"tag":31,"props":241,"children":242},{},[243],{"type":28,"value":71},{"type":28,"value":245}," konsument dostaje ten sam event dwukrotnie. Jak zabezpieczasz się przed\npodwójnym przetworzeniem?",{"type":23,"tag":24,"props":247,"children":248},{},[249,251,256,258,264,266,271],{"type":28,"value":250},"Skoro outbox (i każdy broker at-least-once) potrafi dostarczyć duplikat, konsument musi\nprzetworzyć ten sam event dwa razy ",{"type":23,"tag":31,"props":252,"children":253},{},[254],{"type":28,"value":255},"bez podwójnego efektu",{"type":28,"value":257},". Odruch seniora: dedup po\nidentyfikatorze wiadomości. Konsument trzyma zbiór przetworzonych ",{"type":23,"tag":92,"props":259,"children":261},{"className":260},[],[262],{"type":28,"value":263},"message_id",{"type":28,"value":265}," (tabela\ninbox) i wstawia-i-sprawdza wynik ",{"type":23,"tag":31,"props":267,"children":268},{},[269],{"type":28,"value":270},"w tej samej transakcji",{"type":28,"value":272}," co efekt uboczny:",{"type":23,"tag":135,"props":274,"children":277},{"className":275,"code":276,"language":28,"meta":7,"style":7},"language-text shiki shiki-themes github-dark","odbierz(msg):\n  BEGIN\n    INSERT INTO inbox(message_id) VALUES (msg.id)   -- UNIQUE(message_id)\n      ON CONFLICT DO NOTHING                         -- duplikat → 0 wierszy\n    if wstawiono 0 wierszy: ROLLBACK; return         -- już przetworzone, pomiń\n    zastosuj efekt biznesowy\n  COMMIT\n",[278],{"type":23,"tag":92,"props":279,"children":280},{"__ignoreMap":7},[281,289,297,305,313,321,330],{"type":23,"tag":145,"props":282,"children":283},{"class":147,"line":148},[284],{"type":23,"tag":145,"props":285,"children":286},{},[287],{"type":28,"value":288},"odbierz(msg):\n",{"type":23,"tag":145,"props":290,"children":291},{"class":147,"line":157},[292],{"type":23,"tag":145,"props":293,"children":294},{},[295],{"type":28,"value":296},"  BEGIN\n",{"type":23,"tag":145,"props":298,"children":299},{"class":147,"line":166},[300],{"type":23,"tag":145,"props":301,"children":302},{},[303],{"type":28,"value":304},"    INSERT INTO inbox(message_id) VALUES (msg.id)   -- UNIQUE(message_id)\n",{"type":23,"tag":145,"props":306,"children":307},{"class":147,"line":175},[308],{"type":23,"tag":145,"props":309,"children":310},{},[311],{"type":28,"value":312},"      ON CONFLICT DO NOTHING                         -- duplikat → 0 wierszy\n",{"type":23,"tag":145,"props":314,"children":315},{"class":147,"line":184},[316],{"type":23,"tag":145,"props":317,"children":318},{},[319],{"type":28,"value":320},"    if wstawiono 0 wierszy: ROLLBACK; return         -- już przetworzone, pomiń\n",{"type":23,"tag":145,"props":322,"children":324},{"class":147,"line":323},6,[325],{"type":23,"tag":145,"props":326,"children":327},{},[328],{"type":28,"value":329},"    zastosuj efekt biznesowy\n",{"type":23,"tag":145,"props":331,"children":333},{"class":147,"line":332},7,[334],{"type":23,"tag":145,"props":335,"children":336},{},[337],{"type":28,"value":338},"  COMMIT\n",{"type":23,"tag":24,"props":340,"children":341},{},[342,344,350,352,362,364,370,372,377,379,384,386,391,393,398,400,405,407,412],{"type":28,"value":343},"Strażnikiem nie jest tu ",{"type":23,"tag":92,"props":345,"children":347},{"className":346},[],[348],{"type":28,"value":349},"if",{"type":28,"value":351},", tylko ",{"type":23,"tag":31,"props":353,"children":354},{},[355,357],{"type":28,"value":356},"unikalny indeks na ",{"type":23,"tag":92,"props":358,"children":360},{"className":359},[],[361],{"type":28,"value":263},{"type":28,"value":363}," — samo „sprawdź,\npotem wstaw\" bez niego to race condition: przy dwóch konsumentach mielących ten sam duplikat\nrównolegle oba ",{"type":23,"tag":92,"props":365,"children":367},{"className":366},[],[368],{"type":28,"value":369},"contains",{"type":28,"value":371}," zwracają false (na READ COMMITTED) i efekt leci dwa razy. Dopiero\ninsert w unikalny indeks, który pada \u002F zwraca 0 wierszy, jest realnym dedupem. (Tabela inbox\nnie rośnie w nieskończoność — trzymasz okno deduplikacji z TTL rzędu maksymalnego czasu\nredelivery brokera, starsze ",{"type":23,"tag":92,"props":373,"children":375},{"className":374},[],[376],{"type":28,"value":263},{"type":28,"value":378}," czyścisz.) Całość zamienia\ndostarczanie ",{"type":23,"tag":380,"props":381,"children":382},"em",{},[383],{"type":28,"value":207},{"type":28,"value":385}," w przetwarzanie ",{"type":23,"tag":380,"props":387,"children":388},{},[389],{"type":28,"value":390},"effectively-once",{"type":28,"value":392},". Alternatywnie\n— zrób operację ",{"type":23,"tag":31,"props":394,"children":395},{},[396],{"type":28,"value":397},"naturalnie idempotentną",{"type":28,"value":399}," (upsert po kluczu biznesowym, „ustaw\", nie\n„zwiększ\"). Sedno: w systemie rozproszonym nie wyeliminujesz duplikatów, możesz tylko\nuczynić ponowne dostarczenie tanim. Dlatego senior nie goni za „exactly-once delivery\"\n(mit — Kafkowe „exactly-once\" to ",{"type":23,"tag":380,"props":401,"children":402},{},[403],{"type":28,"value":404},"effectively-once processing",{"type":28,"value":406}," wewnątrz pipeline'u, nie\ndelivery do dowolnego konsumenta) — robi konsumenta idempotentnym. Zastrzeżenie: dedup w transakcji działa, dopóki efekt\nto zapis w ",{"type":23,"tag":31,"props":408,"children":409},{},[410],{"type":28,"value":411},"tej samej bazie",{"type":28,"value":413}," co inbox; gdy efektem jest wywołanie zewnętrznego systemu\n(płatność, mail), transakcja go nie obejmuje — idempotencję przenosisz wtedy na jego granicę\n(idempotency key u odbiorcy).",{"type":23,"tag":58,"props":415,"children":417},{"id":416},"_3-spójność-natychmiastowa-vs-ostateczna",[418],{"type":28,"value":419},"3. Spójność natychmiastowa vs ostateczna",{"type":23,"tag":24,"props":421,"children":422},{},[423,427],{"type":23,"tag":31,"props":424,"children":425},{},[426],{"type":28,"value":71},{"type":28,"value":428}," czym różni się spójność natychmiastowa od ostatecznej? Gdzie świadomie\nwybierzesz tę drugą?",{"type":23,"tag":24,"props":430,"children":431},{},[432,434,439],{"type":28,"value":433},"Natychmiastowa: po powrocie z zapisu każdy czytelnik widzi nową wartość — jedna granica\ntransakcyjna. Ostateczna: czytelnik może przez chwilę zobaczyć stan sprzed zmiany, system\nzbiega do spójności. To, co kupujesz eventual consistency, to ",{"type":23,"tag":31,"props":435,"children":436},{},[437],{"type":28,"value":438},"dostępność, luźne spięcie\ni skala",{"type":28,"value":440}," — usługi nie blokują się nawzajem. Cena: okno nieświeżości, które biznes musi\ntolerować.",{"type":23,"tag":24,"props":442,"children":443},{},[444,446,451,453,458],{"type":28,"value":445},"Przykład, w którym wybieram ostateczną świadomie: aktualizacja modelu odczytu \u002F indeksu\nwyszukiwania \u002F innego kontekstu po złożeniu zamówienia. Samo zamówienie musi zacommitować\nnatychmiast (pieniądze), ale „licznik zamówień na dashboardzie\" czy indeks rekomendacji\nmoże spóźnić się o sekundę. Wciśnięcie ich w transakcję zamówienia sprzęgłoby wszystko,\npogorszyło latencję i sprawiło, że zamówienie ",{"type":23,"tag":31,"props":447,"children":448},{},[449],{"type":28,"value":450},"padnie, gdy padnie indeks",{"type":28,"value":452},". Więc:\nnatychmiastowa tam, gdzie niezmiennik jest naprawdę transakcyjny (saldo w obrębie jednego\nrejestru\u002Fagregatu), ostateczna w poprzek granic kontekstów. Ruch seniora: wybieram ",{"type":23,"tag":31,"props":454,"children":455},{},[456],{"type":28,"value":457},"per niezmiennik",{"type":28,"value":459},", nie globalnie.",{"type":23,"tag":58,"props":461,"children":463},{"id":462},"_4-granice-bounded-contextów-i-mikroserwisów",[464],{"type":28,"value":465},"4. Granice bounded contextów i mikroserwisów",{"type":23,"tag":24,"props":467,"children":468},{},[469,473],{"type":23,"tag":31,"props":470,"children":471},{},[472],{"type":28,"value":71},{"type":28,"value":474}," jak wyznaczasz granice? Po czym poznajesz, że przecięcie jest złe?",{"type":23,"tag":24,"props":476,"children":477},{},[478,480,485,487,492],{"type":28,"value":479},"Tnę po ",{"type":23,"tag":31,"props":481,"children":482},{},[483],{"type":28,"value":484},"zdolności biznesowej i języku",{"type":28,"value":486},", nie po warstwie technicznej. Granica jest dobra,\ngdy kontekst posiada swoje dane i może zmieniać się wewnątrz bez koordynacji z innymi.\nGranica kontekstu to granica ",{"type":23,"tag":31,"props":488,"children":489},{},[490],{"type":28,"value":491},"modelu i języka",{"type":28,"value":493},", niekoniecznie osobny serwis — bounded\ncontext świetnie żyje jako moduł w monolicie; mikroserwis to dopiero decyzja o deploymencie\nnałożona na tę granicę. Sygnały, że przecięcie jest złe:",{"type":23,"tag":495,"props":496,"children":497},"ul",{},[498,509,519,531],{"type":23,"tag":499,"props":500,"children":501},"li",{},[502,507],{"type":23,"tag":31,"props":503,"children":504},{},[505],{"type":28,"value":506},"chatty calls",{"type":28,"value":508}," — dwie usługi wymieniają serię synchronicznych wywołań, żeby wykonać\njedną operację (to naprawdę jeden kontekst),",{"type":23,"tag":499,"props":510,"children":511},{},[512,517],{"type":23,"tag":31,"props":513,"children":514},{},[515],{"type":28,"value":516},"współdzielona encja",{"type":28,"value":518},", którą obie muszą trzymać w zgodzie,",{"type":23,"tag":499,"props":520,"children":521},{},[522,524,529],{"type":28,"value":523},"potrzeba ",{"type":23,"tag":31,"props":525,"children":526},{},[527],{"type":28,"value":528},"transakcji rozproszonej",{"type":28,"value":530},", by utrzymać niezmiennik (niezmiennik przecina\ngranicę → granica jest w złym miejscu),",{"type":23,"tag":499,"props":532,"children":533},{},[534,536,541],{"type":28,"value":535},"zmiana w jednej usłudze ",{"type":23,"tag":31,"props":537,"children":538},{},[539],{"type":28,"value":540},"zawsze",{"type":28,"value":542}," wymusza zmianę w drugiej (wysoki coupling).",{"type":23,"tag":24,"props":544,"children":545},{},[546],{"type":28,"value":547},"Heurystyka: jeśli dwie rzeczy zmieniają się razem i z tego samego powodu biznesowego —\nnależą do siebie. Podział techniczny („wszystkie kontrolery tu, wszystkie repozytoria\ntam\") to klasyczne złe cięcie: każda zmiana biznesowa przecina wszystkie warstwy naraz —\nzero autonomii zmiany (złamane Common Closure Principle).",{"type":23,"tag":58,"props":549,"children":551},{"id":550},"_5-cqrs-kiedy-się-opłaca-a-kiedy-to-overengineering",[552],{"type":28,"value":553},"5. CQRS: kiedy się opłaca, a kiedy to overengineering",{"type":23,"tag":24,"props":555,"children":556},{},[557,561],{"type":23,"tag":31,"props":558,"children":559},{},[560],{"type":28,"value":71},{"type":28,"value":562}," kiedy CQRS ma sens, a kiedy to przerost? Co realnie kosztuje?",{"type":23,"tag":24,"props":564,"children":565},{},[566,568,573],{"type":28,"value":567},"Opłaca się, gdy odczyt i zapis są ",{"type":23,"tag":31,"props":569,"children":570},{},[571],{"type":28,"value":572},"naprawdę asymetryczne",{"type":28,"value":574},": złożone modele odczytu, które\nnie pasują do modelu zapisu; bardzo różne skalowanie czytania i pisania; wiele kształtów\nodczytu z jednego modelu zapisu (raporty, wyszukiwanie, dashboardy). Wtedy rozdzielenie\nmodelu komend i zapytań pozwala każdy zoptymalizować osobno.",{"type":23,"tag":24,"props":576,"children":577},{},[578,580,585,587,592,594,599,601,606],{"type":28,"value":579},"Overengineering, gdy to CRUD o symetrycznych potrzebach — dokładasz drugi model bez korzyści.\nAle uwaga na mit, który sam się tu wkrada: ",{"type":23,"tag":31,"props":581,"children":582},{},[583],{"type":28,"value":584},"CQRS nie znaczy z automatu eventual consistency.",{"type":28,"value":586},"\nW bazowej formie (osobne ścieżki\u002Fmodele komend i zapytań nad ",{"type":23,"tag":380,"props":588,"children":589},{},[590],{"type":28,"value":591},"tą samą",{"type":28,"value":593}," bazą, read model jako\nwidoki SQL albo projekcja w tej samej transakcji) odczyt jest w pełni spójny i tani.\nNieświeżość wchodzi dopiero, gdy rozdzielasz też ",{"type":23,"tag":31,"props":595,"children":596},{},[597],{"type":28,"value":598},"magazyny",{"type":28,"value":600}," — osobna baza odczytu +\nasynchroniczna projekcja. CQRS to spektrum: od osobnych klas w jednej bazie (spójne, tanie)\npo osobne magazyny z projekcjami (skala, ale eventual consistency, „zapisałem i od razu czytam,\na tego jeszcze nie ma\", plus infrastruktura projekcji) — koszt rośnie z każdym krokiem, więc\nzatrzymaj się na najtańszym, który wystarcza. Najczęstszym uzasadnieniem są rozjeżdżające się\npotrzeby ",{"type":23,"tag":31,"props":602,"children":603},{},[604],{"type":28,"value":605},"odczytu",{"type":28,"value":607},"; sam zapis rzadko wystarcza za powód. I drobiazg, który zdradza mida:\nCQRS ≠ event sourcing.",{"type":23,"tag":58,"props":609,"children":611},{"id":610},"_6-transakcje-rozproszone-bez-klasycznej-transakcji",[612],{"type":28,"value":613},"6. Transakcje rozproszone bez klasycznej transakcji",{"type":23,"tag":24,"props":615,"children":616},{},[617,621],{"type":23,"tag":31,"props":618,"children":619},{},[620],{"type":28,"value":71},{"type":28,"value":622}," operacja biznesowa spina kilka serwisów, każdy może paść. Jak zapewniasz\nspójność bez klasycznej transakcji?",{"type":23,"tag":24,"props":624,"children":625},{},[626,628,633,635,640],{"type":28,"value":627},"W praktyce nie robisz 2PC przez granice usług (blokady, coupling, spadek dostępności).\nRezygnujesz z atomowej transakcji i modelujesz operację jako ",{"type":23,"tag":31,"props":629,"children":630},{},[631],{"type":28,"value":632},"sagę",{"type":28,"value":634},": ciąg lokalnych\ntransakcji plus ",{"type":23,"tag":31,"props":636,"children":637},{},[638],{"type":28,"value":639},"akcje kompensujące",{"type":28,"value":641}," cofające wcześniejsze kroki, gdy późniejszy padnie;\nkolejny krok wyzwala event (choreografia) albo komenda koordynatora (orkiestracja). To rollback\nsemantyczny, nie bazodanowy — nie „od-obciążysz\" karty, robisz zwrot. I pamiętaj o cenie, o\nktórej się milczy: saga nie ma „I\" z ACID — między krokami inne transakcje widzą stan pośredni,\nwięc bronisz się środkami zaradczymi (semantic lock, wersjonowanie), a niektórych kroków\n(pivot) nie da się już skompensować.",{"type":23,"tag":24,"props":643,"children":644},{},[645],{"type":28,"value":646},"Dwa style:",{"type":23,"tag":495,"props":648,"children":649},{},[650,660],{"type":23,"tag":499,"props":651,"children":652},{},[653,658],{"type":23,"tag":31,"props":654,"children":655},{},[656],{"type":28,"value":657},"choreografia",{"type":28,"value":659}," — każda usługa reaguje na eventy, brak centralnego koordynatora. Proste,\nale przepływ jest emergentny, trudny do zobaczenia w całości.",{"type":23,"tag":499,"props":661,"children":662},{},[663,668],{"type":23,"tag":31,"props":664,"children":665},{},[666],{"type":28,"value":667},"orkiestracja",{"type":28,"value":669}," — usługa-koordynator prowadzi kroki. Przepływ jawny i „posiadany\", kosztem\ncentralnego komponentu.",{"type":23,"tag":24,"props":671,"children":672},{},[673,675,680,682,687],{"type":28,"value":674},"Choreografia przy kilku krokach i luźnym spięciu; orkiestracja, gdy przepływ jest złożony\ni musisz go widzieć. Klocki: każdy krok musi być ",{"type":23,"tag":31,"props":676,"children":677},{},[678],{"type":28,"value":679},"idempotentny",{"type":28,"value":681}," (pytanie 2) i ",{"type":23,"tag":31,"props":683,"children":684},{},[685],{"type":28,"value":686},"niezawodnie\nopublikowany",{"type":28,"value":688}," (pytanie 1, outbox). Dlatego 1, 2 i 6 to ten sam problem z trzech stron.",{"type":23,"tag":58,"props":690,"children":692},{"id":691},"nić-przez-wszystkie-sześć",[693],{"type":28,"value":694},"Nić przez wszystkie sześć",{"type":23,"tag":24,"props":696,"children":697},{},[698,700,704],{"type":28,"value":699},"Każdy z tych wzorców kupuje własność systemu rozproszonego i płaci konkretną cenę:\nnieświeżością, złożonością, logiką kompensacji, kosztem operacyjnym. Mid nazywa wzorzec.\nSenior nazywa cenę, zanim go o nią zapytają — i wie, kiedy po wzorzec ",{"type":23,"tag":31,"props":701,"children":702},{},[703],{"type":28,"value":49},{"type":28,"value":705}," sięgać, bo\nnajdroższy jest ten, który wprowadzono bez potrzeby.",{"type":23,"tag":24,"props":707,"children":708},{},[709],{"type":28,"value":710},"Tak podchodzę do architektury: pytanie nigdy nie brzmi „który wzorzec\", tylko „co za to\nkupujemy i ile to kosztuje\".",{"type":23,"tag":712,"props":713,"children":714},"style",{},[715],{"type":28,"value":716},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":7,"searchDepth":157,"depth":157,"links":718},[719,720,721,722,723,724,725],{"id":60,"depth":157,"text":63},{"id":233,"depth":157,"text":236},{"id":416,"depth":157,"text":419},{"id":462,"depth":157,"text":465},{"id":550,"depth":157,"text":553},{"id":610,"depth":157,"text":613},{"id":691,"depth":157,"text":694},"markdown","content:blog:szesc-senioralnych-pytan-o-architekture.md","content","blog\u002Fszesc-senioralnych-pytan-o-architekture.md","blog\u002Fszesc-senioralnych-pytan-o-architekture","md",1783499642978]