Na obsah stránky

Repository je anti-pattern

Aleš Roubíček | | # permalink

Žádný vzor není špatný. Vezměme si chudáčka Singletona. Skvělý vzor pro udržení jediné instance v požadovaném scope. Jenže pak se dočtete, že Singleton je neslučitelný s Dependency Inversion Principle. Opravdu? Jak to, že používám DIP i Singletonty už léta? On je problem někde jinde. U lidí co nepřemýšlí. :) Fakt.

Mějme třídu, kde se na pěti místech v jejím kódu vyskytuje HttpContext.Current. Opravdu je to problem Singletonu? Ale kdepak, je to problem v užití, potažmo neschopností dělat čistý design. Přitom stačí nadefinovat členskou proměnnou typu HttpContext (lépe HttpContextBase) a tu si nechat naservírovat pomocí konstruktoru.

Volání HttpContext.Current bude v aplikaci pouze na jednom místě – v Composition rootu aplikace (popř. V konfiguraci IoC kontejneru.) Zbytkem aplikace nám bude protékat právě tato instance.

Doufám, že jsem na těchto pár řádcích dokázal, že Singleton není anti-pattern. Že anti-pattern jsou programátoři.

Říkám repository, myslím Data Mapper

A pomalu se dostáváme k jádru našeho problému. Vzory jsou tu od toho, aby sjednotili slovník, aby každý hned věděl o čem se bavíme. Jenže v případě Repository to došlo tak daleko, že lidé říkají Repository, ale 99 % myslí Data Mapper.

Repository nám schovává datový zdroj za rozhraní kolekce a umožňuje nad ním spouštět doménové dotazy. Světe div se, v dotnetu už něco takového máme. Ano, je to IEnumerable a LINQ!

V praxi se však setkáme s repository, která přímo skládá SQL dotazy, menežuje život entit a má tlusté rozhraní jako prase. Proč? Protože opět selhaly schopnosti návrhu. Zde konkrétně Single Responsibility Principle a Interface Segregation Principle.

Tlustá rozhraní

Tlustá rozhraní u “Repository” vznikají z potřeby zapouzdřit často používané dotazy do znovupoužitelné jednotky. Bohužel i v 21. století je jednotkou znovupoužitelnosti procedura. A tak s každým novým dotazem přidáme na rozhraní “Repository” další a další metodu. To nám spokojeně roste a roste. A opět porušujeme Interface Segregation Principle, Single Responsibility Principle a navíc i Open Closed Principle – a vytváříme krásnou kuličku sraček.

Třída, která toto rozhraní implementuje mívá nezřídka tisíc řádků a dá se v nít “snadno orientovat” pomocí stromečků. Jenže, jak praví Uncle Bob:

“V každé velké metodě se schovávají malé třídy!”

Ale málem bych zapomněl na Liskov Substitution Principle. Určitě jste všichni někdy narazili na potřebu cachovat. :)

Dekorováním za čistší kód!

A tak, když dospějeme k závěru, že tenhle dotaz se provádí dost často a přitom se data takřka nemění, budeme cachovat. První věc, co nás napadne, je nasrat tu cache přímo do repository. Tak to bude transparentí a nikde to nemusím měnit. Au. Lepším řešením je udělat třídu se stejným rozhraním, která v konstruktoru přijímá to samé rozhraní a potřebné metody odekoruje cachováním. Zbylé volá přímo na dekorovaném objektu.

Netvrdím, že jde o dokonalý návrh, ale jde rozhodně o mnohem čistší řešení. A to vše díky dodržování principů SOLIDní architektury. Opakuju se tu o tom už léta, ale asi je to stale potřeba. ;)

Našli jste v článku chybu? Máte námět na reportáž? Založte mi ticket.