Architektura aplikace podle DDD

I když se o návrh architektury aplikací zajímám už nějaký ten pátek, stále si připadám jako bych byl na začátku – inu je to téma, které není vůbec jednoduché a postupem času se navíc komplikuje, jak se dostávají do popředí zájmu nové technologie (NoSQL, HTML5) nebo náhledy na věc (CQRS, messaging, pub/sub). Proto jsem se rozhodl sepsat tento článek, který neberte jako dogma, ale jen jako soupis mých aktuálních myšlenek a chápání architektury aplikace, který by měl rozpoutat diskuzi. Nemyslím si, že by existoval jediný správný přístup, ale o to by mohla být naše diskuze zajímavější :-)

Vymezení pojmů

Většina pojmů, které používám, vychází z DDD. Pokud se podíváme na aplikace třívrstvýma očima, je v tomto článku řeč především o prostřední vrstvě (business, doménové, logické, …) a možná částečně o nejnižší (datové) vrstvě (pokud do ní chceme zahrnout DAL). Není vůbec řeč o prezentační vrstvě (MVC, MVP, MTV, MVVM).

Application Service

Prezentační vrstva by měla komunikovat s prostřední vrstvou (tj. vlastní aplikací) přes Application Service (tak o ní mluví Eric Evans), což je jedno nebo více rozhraní a související DTOs (tedy něco snadno vystavitelného třeba jako WebService). Každá metoda tohoto rozhraní představuje jeden use-case a tedy logickou transakci. Implementace této vrstvy vypadá tak, že se pouze volá Domain Model (o tom více níže).
Martin Fowler o této vrstvě mluví jako o Service Layer (rozkliknout – obsahuje důležitý obrázek!) a myslím, že s ním nejsem v rozporu.
Na tohle téma je můj názor ustálený, ale rád si přečtu i jiné pohledy na věc.

Domain Model

Vlastní jádro aplikace je ukryto v Domain Modelu, což je vrstva, která by měla obsahovat veškerou logiku naší aplikace. Jak by měla ale taková vrstva vypadat?
Klasický DDD přístup je ten, že by to měl být přirozený OO obraz naší aplikace, tj. měly by to být třídy, které nesou nejen data, ale obsahují také chování (doménové metody). Zde vzniká problém, že u některých metod může být složité rozhodnout, ve které třídě by měly být (třeba protože pracují s více entitami). Proto se zavádí pojem Domain Service, což je interface/třída, která obsahuje právě tyto těžko umistitelné metody. Zde se nabízí otázka, proč bychom měli tyto doménové servisy abstrahovat pomocí interfejsů, když jsou ve stejném modulu, který obsahuje doménové objekty, které také neabstrahují své metody. Jediná rozumná odpověď, která mě napadá, je testování.

Na výše popsaném klasickém přístupu se mi nelíbí to, že některé metody jsou přímo v entitách a některé v doménových servisách. Zde máme vzásadě dvě (extrémní) řešení:
1) Budeme se snažit namodelovat naší doménovou vrstvu takovým způsobem, abychom doménové servisy jednoduše nepotřebovali. To může vyústit v to, že budeme mít entity, které budou popisovat nějaký proces (což může být ok), v horším případě ve vytváření umělých entit (de facto tedy doménových servis).
2) Veškerou doménovou logiku přesuneme do doménových servis a z entit nám tak vzniknou jen schránky na data a budeme tak mít anemický doménový model. Jak moc tento název zní nezdravě a mluví se o něm jako o anti-patternu, tak i tento postup může být pro specifickou doménu to nejlepší řešení. Doménové servisy jsou pak vlastně kontejnery na transakční skripty. Sice se tím zřekneme jedné z hlavní myšlenek OOP (že data a chování by měly být u sebe) a de facto se tím vrátíme k procedurálnímu programování, ale nemyslím si, že je to vyloženě špatně.

Co vy na to? Považujete anemický model za zhůvěřilost nehodnou života v 21. století? :)

Persistence

Schopnost ukládat a načítat data z nějakého úložiště se abstrahuje do Repository a slouží tedy k abstrakci úložiště od domény. Co by měla taková repozitory obsahovat? Asi by tu měly být nějaké metody pro načtení a uložení objektu…ale jaké konkrétně? Základní CRUD operace jsou asi samozřejmostí, ale co složitější dotazy? Upřednostňujete Specification, Query Object nebo explicitní metody pro každý dotaz? Co je váš favorit?

A co dotazy reportovacího charakteru? Já bych je dal do read-only QueryRepository – z hlediska CQRS to není nutné, ale IMHO je to velmi vhodné.

A jak by měly vypadat implementace běžných repositories? Pokud pracujeme proti relační databázi, tak je myslím nabíledni použít nějaký DAO, typicky objektově-relační mapper.
Protože repositories vytváří doménové objekty, je třeba vyřešit ještě jednu důležitou věc – tyto objekty mohou mít nějaké závislosti a ty je třeba do těchto objektů nějak dostat. Měla by tedy repository injectovat závislosti do doménových objektů? Nebo by měla tento úkol delegovat na nějakou Factory? Pak ale potřebujeme od DAO, aby nám umožnila řídit vytváření instaní objektů, což nemusí být samozřejmostí…
Co myslíte, kdo by měl zajišťovat injektování závislostí do doménových objektů? A jaké závislosti vůbec mohou v doménových objektech být?

Kapitola sama pro sebe jsou transakce a Unit Of Work. Jak jsem psal v úvodu, tak do jedné transakce bych strčil celou metodu aplikační servisy – pak není problém si naijectovat transakční objekt nebo UnitOfWork do implementace repository.

Shrnutí

V článku jsem se snažil popsat svůj pohled na návrh architektury aplikace, přičemž jsem velmi ovlivněn DDD. Při uvádění těchto principů do praxe jsem přirozeně narazil na několik záseků – ať už kvůli mé neznalosti nebo nepochopení, tak kvůli nejednoznačnosti (existuje více správných cest, každá vhodná v jiné situaci). V textu jsem explicitně vypíchnul jen pár otázek, ale budu rád, když mě opravíte/poučíte i v něčem jiném :-)

Příspěvek byl publikován v rubrice Programování a jeho autorem je Augi. Můžete si jeho odkaz uložit mezi své oblíbené záložky nebo ho sdílet s přáteli.

35 komentářů u “Architektura aplikace podle DDD

  1. Pár nekompletních poznámek:
    - Anemický model není Domain Model, ale jak správně poznamenáváš Transaction Script. Na tom samo o sobě není nic špatného, volba závisí na mnoha faktorech. Transaction Script je daleko jednodušší, ale přicházíš o výhody, které má poskytovat Domain Model: zapouzdření, méně duplicity atp.
    - Z vlastní zkušenosti vím, že udělat dobrej Domain Model je sakra těžká facha. Logika z domény často leakuje do servisní vrstvy (a pak se z toho stává transaction script). Doménové objekty se mohou lehce stát závislé na persistenční vrstvě, což zkomplikuje testy (tady se naplno projeví výhoda test-first přístupu). Doménové služby mohou vytvořit nepřehlednou spleť objektů s neprůhlednými interakcemi. Dál se musí řešit interakce s komponentami na jiných vrstvách (např. nějaká událost v doméně vyvolá odeslání emailu, jenže ten se posílá z jiné vrstvy aplikace, která je fyzicky jinde). Špatný domain model je rezistentní vůči refaktoringu, takže je výhodné to hned na začátku udělat dobře a pak striktně držet styl:-) Je to hodně o zkušenostech, ale kde je má člověk nabrat, když s tím začíná?
    - Persistence: spoustu zmíněných problémů řeší NHibernate. Měl by stačit lehký wrapper nad ním. Např. my máme vlastní query objekty, ale asi to neni nutný, protože něco podobnýho má sám NHibernate. Nebo by měl jít použít Linq2NHibernate.
    - Reportování: Je otázka, jestli by se mělo dělat přes domain model, a jestli vůbec na stejné DB kde běží aplikace.
    - Kdysi jsem dělal nějakou prezentaci jak to řešíme my, nevim jak moc je to srozumitelný, ale něco tam snad vidět bude: http://www.slideshare.net/danielkolman/ginger-1577358

  2. 1) Ad. persistence: NHibernate je super, jenže když člověk nepoužívá v projektu jen relační databáze, ale taky něco nerelačního (moje konkrétní situace), tak to už není tak jednoduché :-) Jak máte reprezentované ty query objekty? V té prezentaci jsem si všiml nějakého vWhere.User.Age, OrderBy.Id, …
    Tohle generujete automaticky (z DSL modelu) a hodnoty jsou přímo názvy sloupečků?

    2) Ad. reporting: Jj, na reporting bych šel asi přímo do té specializované QueryRepository. V doménovém modelu bych jen generoval events/messages, které by si ta QueryRepository zpracovala. Teď je jen otázka, jestli není prasárna, že budu mít v QueryRepository nějakou reportovací logiku. Asi bych s tím dokázal žít :-)

    3) Ad. (pěkná) prezentace:
    a) Remote Façade je prakticky to, o čem mluvím jako o Application Service nebo Service Layer? Nebo máte obě tyhle vrstvy a máte je ve vztahu 1:1?
    b) Message Handlers vám sedí v Domain Modelu nebo Remote Façade?
    c) Rozhraní ven máte teda realizované prostřednictvím Message Handlers, které se volají pomocí nějak customizovaného WCF?

  3. Místo repository pro dotazy se ještě používají „Query objekty“ viz http://rarous.net/weblog/377-domenove-dotazy.aspx

    Osobně používám generické repository pro GetOne(id)/SaveOne(obj)/DeleteOne(obj) a pro dotazy používám „Query objekty“ implementované v prezentační vrstvě (různé prezentace mohou zobrazovat data různě). „Query objekty“ můžou vytvářet dotazy čistě přes Linq, ale dávám jim k dispozici i NHibernatí ISession.

    Použití „Query objektů“ dost odlehčí repository a navíc každý jeden dotaz (a ty dotazy mohou být někdy celkem komplexní) je v samostatné třídě, což je imho přehlednější.

  4. @Augi Jo, query objekty generujeme, vyhneme se tím použití stringů. Query object pak poskládá HQL pro NHibernate. Určitě by to šlo udělat i přes LINQ, ale jednak je to složitější, jednak jsme tenkrát ještě neměli VS2008;)

    Remote Facade + Remote Api = Service Layer = Application Service

    Rozhraní je velmi jednoduché:
    interface IMessageService {
    IMessage Handle(IMessage request);
    }

    Takže pro každou operaci s domain vrstvou existuje jedna třída IMessage. MessageService najde podle typu zprávy správný IMessageHandler where T : IMessage, který sedí v service layer a jeho úkolem je najít doménový objekt, zavolat na něm nějakou metodu a pak vytvořit a vrátit response message. Koncept je vypůjčený z NServiceBus.

    Remote API je jen jedna knihovna, která obsahuje zprávy, DTOs a proxy na message service. Interně je to buď WCF nebo in-process, ale díky tomu jednoduchýmu interface je to dost transparentní (kromě serializačních attributů). Má to tu výhodu, že interface služby je zcela stabilní, jediný co přidáváš a měníš jsou IMessage a IMessageHandler

  5. ad Repository – ja od nich zacal postupne upoustet a v service layer pracuju primo nad NH ISession (duvody viz napr. Ayende blog), resp. pro slozitejsi dotazovani pouzivam zmineny pattern Query object.

    DDD je urcite krasna vec, ale zatim se mi ho v realu nepodarilo dosahnout ;( Uz jen kvuli tomu, ze se slozitejsim business modelem zacnu mit problem se zavislostmi z domain modelu a injektovani zavislosti do modelu povazuju za zlo. Takze v soucasne dobe pouzivam ne uplne anemicky model + service layer + nad tim jeste WCF vrstvu, kterou momentalne prevadim na Agatha RRSL a s tim mozna potom v dusledku dospeju ke splosteni 2 SL do jedne…

  6. Steve:
    Generická repository pro základní operace a něco dalšího pro dotazy – tahle myšlenka se mi docela líbí. Je ale otázka, jak tomu něčemu dalšímu říkat a jaký by to mělo mít interface.

    Když to vezmu tak, jak o tom píše Martin Fowler: A Query Object is an interpreter, that is, a structure of objects that can form itself into a SQL query.

    Query Object jsem doteď chápal jen jako popis dotazu (třeba strom podmínek, klidně Expression) a myslel jsem si, že interpretaci dotazu do řeči konkrétního databázového stroje, by měla zajišťovat repository (tj. že repository by měla mít metodu, která přijímá Query Object), případně IQueryExecutor jako v Alešově případě (jestli jsem to dobře pochopil).
    Teď ale vidím, že jsem se v definici pletl – Query Object by měl být popis dotazu, který se zároveň umí transformovat do SQL dotazu. Abych pravdu řekl, tak to se mi moc nelíbí – raději bych měl tyhle dvě věci oddělené, tj. popis dotazu v Doméně, interpretaci v Infrastruktuře (implementace Repository nebo IQueryExecutor).

    Pokud má Query Object umět oboje (reprezentovat dotaz i ho přeformulovat do SQL), tak je IMHO třeba mít v Doméně pro každý Query Object interface (např. IProductsByCategoryQuery) a v Infrastruktuře (vedle Repository) mít jeho implementaci. Máš to tak nebo nějak jinak? :-)

  7. dkl: Jj, že ven vystavujete tyhle MessageHandlery přes MessageService, to mi je jasný :-) Jen by mě zajímalo, jak je vystavujete ven přes WCF – to máte jen jednu MessageService s jedinou metodou Handle, která na základě typu obdržené zprávy rozhodne, jaký handler zavolat? Takže brutální KnowType? :-)

  8. FilipK: Ayendeho názor znám, ale myslím si, že on opravdu nemá potřebu dále abstrahovat NHibernate (resp. NHibernate je Repository). Jenže já třeba dělám na projektu, kde je asi polovina dat v NoSQL databázi, nad kterou neexistuje IQueryable, takže tam nějaká další vrstva je nutná.
    Závislosti do Modelu jsou i dle mého názoru zlo a proto nemám nic proti téměr-anemickému modelu :-)
    A jak konkrétně máš implementovaný ten tvůj Query Object (viz moje otázka na Steva) ?

  9. Augi: tu tvoji NoSQL cast jsem nejak prehlid – to mas potom s nutnosti dasli abstrakce pres repo pravdu..
    Query object mam celkem primitivni – ciste jen trida drzici pripadne hodnoty filtru/podminek (posilam je i pres WCF). „Spoustec“ dotazu je potom zvlast a je to v podstate jen nadstavba nad NH session.

    kdyz ted koukam na tu Alesovu implementaci, Fowlerovu definici atd, tak mi skoro prijde, ze si kazdy Query object oznacuje neco trochu jinyho :)

    ad dkl+WCF: to je presne to, co resi Agatha (http://code.google.com/p/agatha-rrsl/) – dkl to ma zrejme implementovano po svem, ale princip vypada stejne..

  10. FilipK: Jo, taky mi přijde, že to každej chápe trošku jinak :-) Nicméně se mi nejvíc líbí to, že Query Object je popis dotazu a o jeho spuštení se postará někdo jiný.
    Jak konkrétně máš napsaný ten spouštěč? Má jen jednu metodu, která zpracuje jakýkoliv Query Object, tj. uvnitř metody máš rozskok podle typu Query Objectu? Nebo máš spouštěc pro každý jednotlivý Query Object?

  11. Tak ten rozskok ale de facto děláš – ale resolvingem přes kontejner, že? :-)

  12. Někde ten switch být musí – takhle ho máš pravděpodobně přes nějaký Dictionary v útrobách kontejneru :-)

  13. @Augi „jak je vystavujete ven přes WCF – to máte jen jednu MessageService s jedinou metodou Handle, která na základě typu obdržené zprávy rozhodne, jaký handler zavolat?“

    Jo, konceptuálně jen jedna metoda (viz pozn). KnownType není potřeba, komunikujeme .NET – .NET, takže používáme NetDataContractSerializer kterej si do message přihodí i typ. AFAIK NServiceBus používá vlastní serializaci, která je prý rychlejší.

    S tim switchem mezi SQL a NoSQL bacha na transakce.

    Jo a neni mi jasný proč by injektování dependencí do domain objektu mělo bejt špatný, mně to přijde košer (pokud máte obavy o rychlost, komunikace s DB je řádově pomalejší).

    Pozn: Ve skutečnosti má MessageService metody dvě:-) Jedna se jmenuje ProcessMessage, druhá ProcessMessages. Když se to totiž vystaví přes WCF, je blbost dělat tři volání přes síť kvůli třem zprávám jednoho odesílatele. Takže pomocí té druhé, která jako parametr bere kolekci zpráv, se dosahuje Coarse-Grained Interface, můžeš v jednom volání poslat více zpráv. Tato agregace pak do určité míry může proběhnout sama. Ale to je jen implementační detail;)

  14. Augi: mám to tak, že „Query objekt“ se stará dokonce i o spuštění dotazu a vrácení výsledku. Není to tedy úplně ten Query objekt od Fowlera.

    Jednotlivé Query objekty mám jenom za generickým ifacem IQuery. Nicméně controllery získávají jejich instance s dosazenou NHibernate ISession/EF context/cokoliv jiného přes constructor injection. Do contolleru neinjektuju přes interface, ale rovnou konkrétní typ Query objektu. Je to trade-off mezi složitým vymýšlením jak udělat, aby moje aplikace využila tuhle a tuhle featuru databáze a přitom abych si nepošpinil prezentační vrstvu nějakým db specific kódem. Takhle je ten db specific kód sice v prezentační vrstvě, ale izolovaný v query objektech.

    S čím se všichni potýkáme je hledání kompromisu mezi zapouzdřením (v tomto případě: query objekt se umí sám přeložit na SQL) a decouplingem (query objekt je nezávislý na SQL, ale potom musí mít nějaký SQL builder přístup k jeho datům, takže ztrácíme zapouzdření). To samé v bledě modrém je u Domain Modelu: pokud máme business logiku v servisních třídách, pak musí entity veřejně poskytovat data pro potřeby téhle logiky. Když je ta logika v entitách, je možné dosáhnout lepšího zapouzdření. Mimochodem ne jen tak pro nic za nic umí NHibernate mapovat i na čistě privátní fieldy.

    Pokud by se mi někdy poštěstilo být u natolik komplexního projektu, že by bylo vhodné nasadit plnohodnotné DDD, tak použil sofistikovanější postupy, ale podle mě je ale důležité umět si vybrat adekvátní postup. Chvíli jsem pracoval v jedné firmě, kde chtěl nadšený architekt aplikovat DDD na data-driven aplikace typu „zobraz seznam uživatelů/vymaž/přidej/odeber uživatele“ a myslím si, že si tím akorát přidělali dost práce navíc, aniž by takový přístup přinesl nějaké hmatatelné výhody.

    Mimochodem DDD není jediná cesta, kudy se člověk může vydat: jednou jsem narazil na tenhle framework (http://www.lhotka.net/cslanet/), který naopak upřednostňuje zapouzdření – v jednom objektu má člověk všechny metody (i pro přístup k db), které pracují s danou množinou dat.

  15. @dkl: Supr, díky! :-)
    Ohledně toho injectování do doménových objektů…nešlo mi vůbec o výkon, spíš o „čistotu“, resp. nevím, jak elegantně ty dependence injectovat. To řešíš jak?
    Když ti Repository vrací nějakej objekt, tak by ho měla vrátit „hotovej“, tj. se všema dependencema. K tomu injectování používá tvoje Repository nějakou Factory (~kontejner), která se stará o vytváření objektů se správnými dependencemi?

  16. Jinak jsem už dnes na jiném místě citoval tohle, což je v souvislosti s DDD pikantní a trefné, i když autor článku o DDD ani Evansovi nemluví.:)

    „Layers can make for great abstractions. But remember: abstractions are illusions! Beautiful, clean, elegant abstractions are still illusions.
    Don’t be afraid to „cheat“ for example when you need better performance and poke through a layers formal boundaries. This is the essence of programming.
    Once you have a layered architecture don’t be afraid to hack, just be elegant and rigorous about how you surface your hacks to your consumers.“

    Zdroj: http://stevenblack.com/PTN-Layers.html

  17. Augi: Možná by bylo nejlepší, kdybychom spolu nějaký projekt napsali a u toho se kreativně hádali :)

  18. Obávám se, že bys mě jednoznačně převálcoval (což by mělo velký přínos pro mě a nulový pro tebe :)).
    Začal jsem ale přemýšlet, že bych udělal takovou „soutěž“ – napsal bych ultra-hnusně nějaký mikroprojekt a úkolem soutěžících by bylo ho co nejlépe přepsat/zrefaktorovat. Nešlo by o vítěze, ale o konfrontaci různých názorů/přístupů.
    Nějaké náměty na mikroprojekt? :-)

  19. Také pár poznámek:

    „Prezentační vrstva by měla komunikovat s prostřední vrstvou (tj. vlastní aplikací) přes Application Service (tak o ní mluví Eric Evans), což je jedno nebo více rozhraní a související DTOs (tedy něco snadno vystavitelného třeba jako WebService). “

    Já tohle nazývám UI fasády, asi jsme se o tom spolu bavili – Application service je podle mě v DDD mnohem obecnější a není vázána na žádný druh uživatelského rozhraní ani na web service. UI fasáda = vrstva složená z metod, které realizují uživatelské scénáře pro aktuální verzi aplikace.

    „A co dotazy reportovacího charakteru? Já bych je dal do read-only QueryRepository – z hlediska CQRS to není nutné, ale IMHO je to velmi vhodné.“

    O tomhle v DDD mluví Boundary context – na data mohou existovat různé pohledy a pro reportování (zcela odlišný Boundary context od Boundary contextu scénárů nad business objekty) je možné přímo volat třeba metody na DB vrstvě. Jinými slovy, nejen že obcházení business modelu ve scénářích, kde se data čtou, je možné, ale v DDD přímo žádoucí.

    Ad repository:
    Podle mě se chápe pod repository několik odlišných věcí:
    1) Jednoduchý nástroj, který obsahuje dvě ministrategie – jednu pro vytažení dat z DB (přes nějakou, dále mnou nespecifikovanou db vrstvu) a druhou pro filtrování objektů v paměti. Hlavní odpovědností této Repository je odstínit klienta od znalosti, odkud se čtou data. Tato Repository se velmi často kombinuje s metodami přijímajícími objekt Query. Objekt Query je obecný a na žádné technologii nezávislý popis dotazu/filtru – za vygenerování dotazu pro konkrétní databázi nebo nad kolekcí objektů v paměti je odpovědná Repository, přesněji řečeno Interpretery, které používá. Dnes bychom mohli Repository brát jako objekt, který odstiňuje i od přístupu od NoSQL dataáze. V jednom svém řešení jsem měl Repository, která do jednoho business objektu potřebovala dostat data z různých db, datových zdrojů, xml souborů a ppro sloučení dat do objektu používala ještě objekty, které jsem nazýval „agregátory“. Repository je i identitní mapa.

    2) Repository je fasáda pro přístup k datům v databázi a současně je identitní mapou. Tato fasáda data nejen vrací, ale také třeba přes Data mappery ukládá. Buď Repository potom hodně splývá s rozhraním Data mapperu nebo je sama jiným pojmenováním Data Mapperu (ověřeno na cizích projektech).

    3) Za Repository je považován přímo Context/Session ORM – Context je nejen objektem Repository, zpracovatelem LINQ (query), ale také Identitní mapou a Unit of work. Takový context je součástí UI fasád a většinou jeden scénář v UI fasádě = 1 kontext = 1 Unit Of Work = 1 Transakce. Z určitého úhlu pohledu a s velkou nadsázkou jde o transakční skript na steroidech.:)

    A pak různé hybridní Repository z vydestilovaných předcházejících variant.:)

    Anemický model nedělám, krabičky na data nesnáším. Jsou ale aplikace, hlavně některé webové, kde anemický model je spíš přirozený model. Primitivní ale občas i „eye candy“ zobrazovačky dat.
    Jednoduše, kde business logika není, ani business služba nebere.:)

    Transakční skripty (skoro) nepoužívám.

    Důvodem pro zavedení další business služby je pro mě mj.
    1) Nemohu nalézt žádnou třídu, do které by nová operace patřila.

    2) Operace pracuje s vícero objekty a žádný nevystupuje jako přirozený „mistr scénáře“.

    3) Vyjde najevo, že v dalších verzích se operace často mění a je nutné kvůli ní měnit stále třídu i spolupracující objekty. Tuto variantní (=často měněnou část) separuju z business objektů a přemístím do business služeb.

    4) Metoda je kandidátem na použití vzoru Strategy/State.

    5) Business služba by měla být podle mě bezstavová, jediným stavem metody je externí stav v argumentech, který je platný jen po dobu volání metody.

    Bylo by toho ještě hodně, těm UI fasádám a službám se věnuju hodně na kurzu OOP II.;)

  20. Tak díky, ale já fakt válcuju rád těžké váhy, ménu egu to dělá dobře, i když se z toho často zpovídám;) A ty jsi navíc slušný černý kůň:)

    Ten mikroprojekt je dobrý nápad, já jsem používal při přijímání/zaučení vývojářů „knihu jízd“. Ten projekt je dostatečně malý, ale dá se na něm vyzkoušet, jak lidi myslí, které vrstvy používají jestli a jak píšou testy, jestli zvládají algoritmy apod.

  21. René: Kniha jízd je super nápad! Nevyhrabal bys mi přesné zadání, které jsi používal? ;-) Pokud ještě používáš, tak asi ne, abych ti neznehodnotil googlující uchazeče ;-)

    Aleš: Díky, měl jsem v nastavení WordPressu UTC+2 (asi můj hack kvůli letnímu času), ale koukám, že posun v nastavení na UTC+1 se zpětně neprojevil…asi lamky neukládají datumy jako slušní lidé v UTC :-)
    Ovšem ortodoxní WebFormář mě dostal do kolen :-)

  22. Fajne, jsem rad za kazdy cesky clanek o DDD :) A souhlasim v prvni rade s tvrzenim prvniho komentujiciho, ze dobry DM je zatracene tezka prace. Smutne je, ze prave pri tomhle zjisteni si dost vyvojaru radsi rekne, ze DDD je nesmysl a „ze to prece nemuze fungovat“…

  23. Augi: Knihu jízd ještě občas používám, ale neměl by být problém udělat zadání podobného typu.;)

  24. @Augi Teď to nijak neinjektujeme, používáme registry třídy, což je singleton v jehož properties jsou servicy/factory (který tim pádem musej bejt thread safe a stateless). Ale neni to moc dobrý, podle mě je lepší to injektovat přímo do objektů, protože jsou pak závislosti objektu explicitně daný. A taky se to pak líp testuje. Až bude čas, chtěl bych si pohrát s nejakym IoC frameworkem.

  25. @dkl: Tak to se mi taky nezdá jako pěkný řešení z hlediska návrhu…na druhou stranu ale nemusíte řešit třeba problémy s tím, že by vaše DAO neuměla customizovat vytváření instancí (tj. delegovat vytvoření instance na nějaký DI kontejner).
    Třeba s Entity Frameworkem to může být problém, protože ten zdá se neumožňuje řídit vytváření entit (CreateObject není virtuální), leda použít trik s ObjectStateManagerem :(

  26. No je to téměř to, co hledám :-) Na Property Setter Injection by to byl ideální kandidát, ale já mám nějak radši Constructor Injection :(

  27. René: Jj, taky začínám pomalu chápat, že injectování přes properties má svůj smysl…
    FilipK: Jj, v NH to problém není, to vím, jen u toho EF mě to trošku mrzí. Ale chápu, že injectování přes property je „lepší“, když si uvědomím, že je tu možnost, že místo mé třídy můžu dostat jen nějakou proxy…

Napsat komentář

Vaše emailová adresa nebude zveřejněna.

Můžete používat následující HTML značky a atributy: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>