webForumDet fria alternativet

Arkitektur (städdags)

.NET

61 svar · 2 845 visningar · startad av rhdf · sida 2 av 4

Frågan, av rhdf

Jag tänkte sätta mig och börja sanera upp i koden på ett av mina små "hobbyprojekt", en webbshop. I dagsläget så kör ett par sidor olika versioner av den, men jag tänkte nu sätta mig och äntligen bygga "kärnan" i systemet så generiskt som möjligt. Nåväl Om man tar en så pass enkel(?) sak som en produkt. idag har jag en funktion, GetProductByID i klassen ProductsBLL, den Returnerar ett objekt av t

Läs frågan i sin helhet →
Medlem sedan okt. 200850 inlägg
#21

Det finns inga regler eller något definierat att Repositories tillhör domänlagret (Många som säger att dom kör med DDD tycker det är en självklarhet att dom gör det, andra tycker inte det).. frågan är om det är viktigt och jag tror inte det, det viktiga är att man enkelt hittar en modell som passar en bäst för att lätta lokalisera sin kod. Med erfarenhet av stora system (tex Stenas fraksystem) där tyvärr inte ORM användes, så blev det att Repo låg i samma assembly som Entiter. Sedan har det skapat en bra struktur för mig. Har man en OR-mapper så kommer man lätt ifrån crik.refs.

Om Mock, syftade på detta: "där man kan skicka in en parameter om det skall vara mock eller äkta repositorys som man skall använda sig av."

Medlem sedan maj 20012 812 inlägg
#22

fredrik skrev:

Som jag skrev tidigare så går det bra att lägga Repositories i egna Assembly, men erfarenhet i stora system där inte OR-mappers har använts pga policies etc, så har det ofta uppkommit cirkulära referenser när man har valt att lägga dom separat från samma Assembly som sina entiteter.

Om du lägger interfacet till dina repository i samma assembly som din entiteter (alltså i domänen) och använder latebinding för att skapa dina repository i en RepositoryFactory, så slipper du problemet med cirkulärar referenser. Även om du lägger lazyloading i din entiteter med anrop direkt till repositoryn, eftersom du så fall bara har en referens till interfacet i dina entiteter...

fredrik skrev:

frågan är om det är viktigt och jag tror inte det, det viktiga är att man enkelt hittar en modell som passar en bäst för att lätta lokalisera sin kod.

Helt rätt, och som tur är så är vi alla lite olika och det gör det hela intressant att diskutera

- M

Medlem sedan jan. 20022 440 inlägg
#23

Gladh skrev:

Helt rätt, och som tur är så är vi alla lite olika och det gör det hela intressant att diskutera

- M

(y)

Medlem sedan okt. 200850 inlägg
#24

Gladh skrev:

Om du lägger interfacet till dina repository i samma assembly som din entiteter (alltså i domänen) och använder latebinding för att skapa dina repository i en RepositoryFactory, så slipper du problemet med cirkulärar referenser. Även om du lägger lazyloading i din entiteter med anrop direkt till repositoryn, eftersom du så fall bara har en referens till interfacet i dina entiteter...

Jag skulle valt att ha interfacen där jag har min implementation av mina Repositories.. för om man har väl definierade guidlines där man alltid ska gå via en Factory, så kommer aldrig någon bry sig om vart dom finns (man gömmer dom för användaren bakom en fabrik). Vill man har tydlig seperation of concerns så placerar man interface + Repo i en separat assembly och i det fallet så lägger man gärna inte interfacet för Repos bland implementationen av entiter och implementationen av själva Repos i annan assembly. För Repos är "bara" en brygga mellan datakällan och domänmodellen. Jag kan se en vinst att vilja separera alla interface från implementation till egna assemblies..

Men åter igen, det spelar inte så stor roll vart du har dom, huvudsaken är att du och andra kan lätt hitta dom.

Medlem sedan aug. 20003 575 inlägg
#25

Mitt val hade varit att köra interfacen till repositories i Domänmodellen och möjligen lägga ut implemmentationen av dom i en annan assembly men inte om jag inte har någon riktigt bra anledning och sedan för att lösa stubproblemet använda en IoC Container som säger vilken implemmentation av repositoryn som skall användas för vilket interface. Jag tycker det känns onödigt att bryta ut kod i en massa olika projekt bara för uppdelningens skull isåfall räcker det mer ett annat namespace för mig.
Även om det inte är någon big deal så skadar det prestandan pyttelite, samt byggtiden men det är ju inte relevant i denna frågan.

Det handlar väl mest om smak antar jag :).

Medlem sedan maj 20012 812 inlägg
#26

fredrikn skrev:

Vill man har tydlig seperation of concerns så placerar man interface + Repo i en separat assembly

I min värld så lägger man interfacet på 2 olika ställen.

Alternativ 1 är att lägga interfacet i ett eget assembly och att klasserna som skall implementera det ligger i eget assembly. Här måste man dock ändra så att interfacen inte har några som helst relationer till klasser utan bara till interface i sitt eget assembly eller till refererade assemblys. Det kräver mycket mer av utvecklaren men i gengäld så kan interfacet återanvänds i flera olika projekt.

public interface IProductRepository{
  IEnumerable<IProduct> GetProducts();
}

Alternativ 2 är att man lägger interfacet där man tänker använda det, och låter sedan de klasser som behöver implementera interfacet ha en referens ditt, och då utgår man ifrån att dessa klasser inte kommer att återanvändas någon annanstans. Och man kommer lite billigare undan.
Alltså interfacet kommer bara användas av domänlagret eller lager som refererar till det, och jag har inga problem med att skicka in en Product till mitt IProductRepository eftersom jag vet att inget annat projekt kommer använd sig av mitt IProductRepository interface, men igengäld så har jag fått ett repository som är enkelt att byta ut snabbt och enkelt om det behövs. En praktiskt medelväg om ändå dock inte helt korrekt enligt "de lärde"...

Ja jag vet att man skall koda mot Interface och inte mot objekt, men ärligt talat hur många tar alla sina entiteter/value objects och implementerar som interfaces... inte jag iallafall. Inte förren jag måste det, fast man borde kanske testa någongång, bara för att se hur snygg solutionen blir :)

public interface IProductRepository{
  IEnumerable<Product> GetProducts();
}

Att lägga interfacet i samma assembly som Repositoryt gör att jag inte lika enkelt kan byta ut det, och om jag vill byta ut det så måste mitt nya Repository ha en referens till mitt gamla repository bara för att kunna implementera interfacet... nej det är ingen bra lösning (IMHO), även om du får interfacet placerat på samma ställe som en av dina repository-lösningar...

Samma sak sker med exceptions och events, de kommer också att hamla i domänlagret (om man inte väljer att ha interface på alla sina klasser i domänlagret), eftersom de också kommer att referera till entiteter och kommer att användas i entiteterna.

- M

Medlem sedan okt. 200850 inlägg
#27

Gladh skrev:

Att lägga interfacet i samma assembly som Repositoryt gör att jag inte lika enkelt kan byta ut det, och om jag vill byta ut det så måste mitt nya Repository ha en referens till mitt gamla repository bara för att kunna implementera interfacet... nej det är ingen bra lösning (IMHO), även om du får interfacet placerat på samma ställe som en av dina repository-lösningar...

Se på tex .Net Framework, Microsoft lägger inte collection interfacen i en separat assembly där andra typer av klasser ligger implementerade. Om man vill separera Repo från modellen för en tydlig seperation of concern, så tycker jag inte Repos interface ska ligga i samma assembly som entiterna, för då blir man spretig och får inte en tydlig samling av sina "kod" samt får fler dependency mellan assemblies.).

Själv är det mer logiskt att lägga interfacen i samma assembly som "grund" implementation av mina Repos ligger, presic som MS gör med IList<T>, ICollection<T> etc, samma sak med ADO.Net, de som skapar "nya" Connection klasser etc, går mot samma assembly som SqlConnection ligger för att komma åt interfacen. Om du idag ska skapa en egen ICollection.. så kommer du gå mot System.Collections.Generics för du vet att dom ligger där och du kommer till stor sannolikhet leta där först.. Om en annan utvecklare ska bygga enga Repos, så tror jag att dom kommer först titta i din assembly som kanske heter... xxx.Repositories.dll efter interfacen och inte xxxx.DomainLayer.dll, eller va tror du?

Finns ett verktyg som heter NDepend för att se beroende på namsepace och assemblies, ett resultat som ofta visade sig när man körde NDepend var följande:

"If you have concrete implementations of interfaces in the same namespace as the interface you will have loosely coupled code, but a potential bidirectional dependency on the namespace. The solution is to place interfaces and implementations in separate namespaces. I find this useful sometimes but I don't think is it should be a general rule." - Torkel

Patrick Smachia (Skaparen av NDepend) säger:

"Where possible do namespaces instead of assemblies."
"Use namespace hierarchy in order to have fine-grained small components (500/1000 LOC)."
"Levelize your current architecture and continuously check for new dependency cycles."

Han säger också:

"Assemblies are a common form for packaging .NET components. The more I analyze real-world applications, the more I notice that assemblies are over-used. Assemblies are costly to development because they represent a physical notion. We conclude that controlling dependencies between namespaces is the key to reducing the number of assemblies and increasing the number of namespaces. Benefits are: Lighter build process, Optimized compilation time, Lighter deployment, Better startup time for our applications,
Facilities for hierarchical components, Facilities for more finely-grained components"

I tidigare projekt så skapade jag ofta flera assemblies, men såg ingen fördel med det, såg större fördel att skapa så få som möjligt och istället använda namespace. Detta för att tex minska på dependency till assemblies och enklare utföra deployment utan att vara orolig att beroenden till andra assemblies kunde påverkas etc.

Medlem sedan aug. 20003 575 inlägg
#28

fredrikn skrev:

Se på tex .Net Framework, Microsoft lägger inte collection interfacen i en separat assembly där andra typer av klasser ligger implementerade. Om man vill separera Repo från modellen för en tydlig seperation of concern, så tycker jag inte Repos interface ska ligga i samma assembly som entiterna, för då blir man spretig och får inte en tydlig samling av sina "kod" samt får fler dependency mellan assemblies.).

Själv är det mer logiskt att lägga interfacen i samma assembly som "grund" implementation av mina Repos ligger, presic som MS gör med IList<T>, ICollection<T> etc, samma sak med ADO.Net, de som skapar "nya" Connection klasser etc, går mot samma assembly som SqlConnection ligger för att komma åt interfacen. Om du idag ska skapa en egen ICollection.. så kommer du gå mot System.Collections.Generics för du vet att dom ligger där och du kommer till stor sannolikhet leta där först.. Om en annan utvecklare ska bygga enga Repos, så tror jag att dom kommer först titta i din assembly som kanske heter... xxx.Repositories.dll efter interfacen och inte xxxx.DomainLayer.dll, eller va tror du?

Finns ett verktyg som heter NDepend för att se beroende på namsepace och assemblies, ett resultat som ofta visade sig när man körde NDepend var följande:

"If you have concrete implementations of interfaces in the same namespace as the interface you will have loosely coupled code, but a potential bidirectional dependency on the namespace. The solution is to place interfaces and implementations in separate namespaces. I find this useful sometimes but I don't think is it should be a general rule." - Torkel

Patrick Smachia (Skaparen av NDepend) säger:

"Where possible do namespaces instead of assemblies."
"Use namespace hierarchy in order to have fine-grained small components (500/1000 LOC)."
"Levelize your current architecture and continuously check for new dependency cycles."

Han säger också:

"Assemblies are a common form for packaging .NET components. The more I analyze real-world applications, the more I notice that assemblies are over-used. Assemblies are costly to development because they represent a physical notion. We conclude that controlling dependencies between namespaces is the key to reducing the number of assemblies and increasing the number of namespaces. Benefits are: Lighter build process, Optimized compilation time, Lighter deployment, Better startup time for our applications,
Facilities for hierarchical components, Facilities for more finely-grained components"

I tidigare projekt så skapade jag ofta flera assemblies, men såg ingen fördel med det, såg större fördel att skapa så få som möjligt och istället använda namespace. Detta för att tex minska på dependency till assemblies och enklare utföra deployment utan att vara orolig att beroenden till andra assemblies kunde påverkas etc.

Håller med till 100%, som jag skrev tidigare, jag delar hellre upp med namespaces än att ha flera assemblies. Tidigare delade jag dock upp det mesta i assemblies men tycker att det blir jobbigt med flera projekt, samt att det gör att det tar längre tid att bygga ^^

Det är först när jag ser behovet av uppdelning som det börjar bli relevant.
t.ex. Core och User Interface kanske inte behöver ligga i samma p.g.a. jag kommer kanske ha flera User Interfaces längre fram.

Medlem sedan maj 20012 812 inlägg
#29

Fredrik nu pratar vi nog olika användningsområde för interfacen. IList exempel är ju inte till för att MS skall snabbt kunna byta ut sin List-klass mot en annan klass. Utan mer för att beskriva beteende för klasser.

När vi prata interfacen till IRepository så använder vi interfacen för att vi enkelt skall kunna byta ut vårt MySQL-Repository mot ett SQL-Repository eller WS-Repository. Och om du då först skapar ditt MySQL-Repository i ditt domänlager och så får du betydligt svårare att "byta ut det", visst du kan ju alltid skriva ditt nya SQL-Repository i ditt domänlager också.. :).

Om du lägger ditt MySql-Repository som ett eget assembly så kan du enklare byta ut det mot ett Sql-Repository om du använder dig av IRepository. Men om du lägger IRepository interfacet i MySql-Repositoryt så måste ju ditt Sql-Repository ha en referens till MySql-repository och det är inte speciellt bra.

Dessutom var lägger du dina "fake" repository? Ligger de också i domänlagret, eller ligger de i en eget assembly? För de om något är inget som man vill ha med i produktionsmiljön, bättre att få ett slag direkt över fingrarna om man "glömt" att peka om så man inte använder sina fakerespository, än att råka upptäcka att det bara är test data som man kör på i produktion....

- M

Medlem sedan juni 20003 076 inlägg
#30

Intressant diskussion detta! :)
Jag försöker lära mig vad ett repository är, fungerar och vad det bör innehålla. Och efter lite skön läsning har jag kommit fram till att jag använder en variant av repository redan idag utan att ha vetat om det!
Nu hittade jag denna sida: http://arcware.net/nhibernaterepository/
Kan inte någon av er ta en snabbkik och kolla om det är en bra grund för ett repository mot nhibernate?! :)

Medlem sedan okt. 200850 inlägg
#31

Gladh skrev:

Fredrik nu pratar vi nog olika användningsområde för interfacen. IList exempel är ju inte till för att MS skall snabbt kunna byta ut sin List-klass mot en annan klass. Utan mer för att beskriva beteende för klasser.

När vi prata interfacen till IRepository så använder vi interfacen för att vi enkelt skall kunna byta ut vårt MySQL-Repository mot ett SQL-Repository eller WS-Repository. Och om du då först skapar ditt MySQL-Repository i ditt domänlager och så får du betydligt svårare att "byta ut det", visst du kan ju alltid skriva ditt nya SQL-Repository i ditt domänlager också.. :).

Om du lägger ditt MySql-Repository som ett eget assembly så kan du enklare byta ut det mot ett Sql-Repository om du använder dig av IRepository. Men om du lägger IRepository interfacet i MySql-Repositoryt så måste ju ditt Sql-Repository ha en referens till MySql-repository och det är inte speciellt bra.

Dessutom var lägger du dina "fake" repository? Ligger de också i domänlagret, eller ligger de i en eget assembly? För de om något är inget som man vill ha med i produktionsmiljön, bättre att få ett slag direkt över fingrarna om man "glömt" att peka om så man inte använder sina fakerespository, än att råka upptäcka att det bara är test data som man kör på i produktion....

- M

Om IList, andra använder den för sina egna implementationer av listor.

Till 95% så kommer du inte behöva byta ut dina Repo, gör du det så kan du enkelt ändra dom du redan har, eller använda dig av Visitor Pattern. Eller tex använda ADO.Net EF, som kan gå mot olika datakällor, eller ditt egna alternativ. Det jag försöker säga är att det spelar ingen roll om "orginal" Repos ligger i den assembly där dess interface ligger, eller separat, går lika lätt att byta ut. Alla Repos vi skapar kommer att ändå behöva ha en ref till våra entiteter, sedan kör vi med Factories som "gömmer" Repositories faktiska existens.

När det kommer till Stubbar ("Fake" Repo) Repos, så lägger jag dom till mitt TestPorjekt för jag tycker dom hör hemma där. En snabb bild på hur jag brukar skapa mina projekt:

Nsquared2.<SubSystem>.DomainLayer
               Entities
               Repositories
                     <CustomerRepsitory>
                        Interfaces
                                <ICustomerRepository>

Nsquared2.<SubSystem>.TestProject
               Stubs
                      StubCustomerRepository

Samla mycket med försiktighet i en assembly och kör med namspace för att minska på assemblies. OBS! Tidigare separerade jag Repos till en egen Assembly, men efter att jag har jobbat i projekt med massor av class libs i en solution, så gick jag över till mindre assemblies och mer namespace istället, för att slippa massor av dependency referencer. Nu kör jag med denna modellen även för små projekt, mer av vana och att jag trivs med den.

Skulle min Infra förändras i Repos, så är det stor sannolikhet att hela min app kommer alltid köra med den nya. Då kan jag lika gärna uppdatera mina Repos jag har, går snabbare än att skriva nya. Skulle det vara så att jag vill ha kvar mina orginal Repos, så skapar jag en ny assembly som refererar till Nsquared2.DomainLayer.dll vilket den måste för att komma åt entiteterna. Min Repos-factory tar hand skapandet av rätt Repo, tex:

var customerRepository = RepositoryFactory.Create<ICustomerRepository>();

Sedan i tex XML står det:

<object id="ICustomerRepository" type="Nsquared2.<SubSystem>.Repositories.CustomerRepository, Nsquared2.<SubSystem>.DomainLayer.dll"/>

Där det går att konfigurera vilken instans min ICustomerRepository är mappad mot.

Men återigen, det är en smak sak, det finns inget rätt eller fel med det du gör eller det jag gör. Mitt är ett arv från stora apps och jag föredrar så få assemblies som möjligt, för jag gillar inte att ha massa dependency ref och .dller som ska deployas, utan så få som möjligt, och sedan pga det Patrick Smacchia säger om assemblys vs namespace. Jag tror inte vi kommer någon vart längre.. Du har hittat en modell som du tycker funkar bra för dig, jag har hittat ett en funkar bra för mig, det är huvudsaken. Men självklart är diskutionen intresant varför olika väljer att göra på olika sätt.

Medlem sedan okt. 200850 inlägg
#32

doggelito skrev:

Intressant diskussion detta! :)
Jag försöker lära mig vad ett repository är, fungerar och vad det bör innehålla. Och efter lite skön läsning har jag kommit fram till att jag använder en variant av repository redan idag utan att ha vetat om det!
Nu hittade jag denna sida: http://arcware.net/nhibernaterepository/
Kan inte någon av er ta en snabbkik och kolla om det är en bra grund för ett repository mot nhibernate?! :)

Hans lösning är en mer genrik repository och kommer inte klara av alla scenarios.

Här skirver Martin Fowler om Repository Pattern: http://martinfowler.com/eaaCatalog/repository.html

Ett Exempel på en Repository imp med EF skulle kunna se ut:

public class CustomerRepository : ICustomerRepository, IDisposable
{
       private MyObjectContext _context;
       private bool _contextReused = false;

       public CustomerRepository()
       {
       }

       public CustomerRepository(MyObjectContext context)
       {
               _context = context;
               _contextReused = true;
       }

       public void Dispose()
       {
              ReleaseMyObjectContext();
       }

       private MyObjectContext GetMyObjectContext()
       {
             if (!_contextReused)
                    return new MyObjectContext();

             return _context;
       }

       private void ReleaseMyObjectContextIfNotReused()
       {
             if (!_contextReused)
                 ReleaseMyObjectContext();
       }

       private void ReleseMyObjectContext()
       {
               _context.Dispose();
               _contextReused = false; 
       }
   
       public Customer GetCustomerByID(int ID)
       {
              var context = GetMyObjectContext();

              return (from customer in context.CustomerSet
                        where customer.ID == ID
                        select customer).SingleOrDefault();

              ReleaseMyObjectContextIfNotReused();
       }

       public IEnumerable<Customer> GetCustomers()
       {
              var context = GetMyObjectContext();

              return (from customer in context.CustomerSet
                        select customer).ToList();

              ReleaseMyObjectContextIfNotReused();

       }

       public void Update(Customer customer)
       {
              var context = GetMyObjectContext();

              context.Attach(customer);
              context.ApplyPropertyChanges(customer);

              if (!_contextReused)
                  context.SubmitChanges();

              ReleaseMyObjectContextIfNotReused();
       }
}

var customerRepository = new CustomerRepository();

var customer = customerRepository.GetCustomerByID(10);

OBS! Repositoryt ovan är inte 100% implementerat, utan snabb kod jag slängde ihop nu för att visa en enkel demo, tex kör man med IDisposable så ska inte inte implementeras riktigt så som jag gjort. Sedan är det inte alls säkert att vi vill återanvända ett ObjectContext, och då finns det inga skäl att kunna göra en Constructor Injection.

Om du vill tex gömma dina Repositories för en användare och även använda dig av Singelton Pattern så kan du använda dig av Factory Pattern för att skapa upp en Repository, men OBS! Kör inte med Singelton Pattern eller Factory (går men krångligt) när du har fasader eller Services som ska kunna återanvända nHibernates Session eller EF ObjectContext. För då behöver du skicka in (Dependency Injection) dina Session (nHibernate) eller (DataContext) om du kör med EF till dina Repositories, här är ett exempel på en fasad där vi alltid ska skapa en ny kund varje gång vi lägger en order:

public void CreateNewOrder(Order order)
{
             //Anväd Transaction scope om du inte explicit anropar SubmitChanges.

         //using(var transactionScope = new TransactionScope())
         //{
              using(var context = new MyObjectContext())
              {
                     var customerRepository = new CustomerRepository(context);
 
                     cutomerRepository.CreateCustomer(order.Customer);       
  
                     var orderRepository = new OrderRepository(context);

                     orderRepository.CreateOrder(order);

                     context.SubmitChanges();
              }
         //}
}

Du kan även skapa en fasad där du ignorerar användandet av Session eller ObjectContext, fördelen att återanvända dom är utnyttjandet av tex Identity Map eller Unit of Work "UoW" (Vill man utnyttja UoW i tex EF så ska inte anropa metoden SubmitChanges i dina Repositories, utan det ska ske i fasaden. Va försiktigt med detta, kräver att du får det vattentät.

Hoppas detta va till någon hjälp?!

Medlem sedan feb. 200563 inlägg
#33

doggelito skrev:

Intressant diskussion detta! :)
Jag försöker lära mig vad ett repository är, fungerar och vad det bör innehålla. Och efter lite skön läsning har jag kommit fram till att jag använder en variant av repository redan idag utan att ha vetat om det!
Nu hittade jag denna sida: http://arcware.net/nhibernaterepository/
Kan inte någon av er ta en snabbkik och kolla om det är en bra grund för ett repository mot nhibernate?! :)

Jag tog en snabbtitt på metodsignaturerna, och konstaterade att de exponerar ett repository-API som är specifikt för NHibernate. Jag syftar t.ex. på denna metod:

public static Collection<T> Find<T>(string hql)

Visserligen skulle man kunna behålla metodsignaturen (bortsett från att man i så fall borde uppdatera parameternamnet med bättre semantik, t.ex. "string sql" eller "string xpath") om man byter implementation till vanlig SQL, eller kanske XPath strängar om man skulle använda XML istället för en RDB för persisteringen.

Klientkoden som anropar ett repository API bör dock kunna fortsätta användas som tidigare även om man väljer att byta implementation, vilket lär bli svårt om man har förutsatt en NHibernate-implementation och den anropande klientkoden är implementerad med att skicka in diverse HQL-strängar i anropen...

Jag skulle snarare kalla klassen för en NHibernate utility klass än att hävda att den är ett DDD repository.

Jag tycker också att boken om Domain-Driven Design är ganska tydlig angående att inte exponera ett API som är infrastruktur-specifik, bl.a. i de fetstilta avsnitten i repository-kapitlet, t.ex. här är ett citat från sidan 150:
"Exposure of technical infrastructure and database access mechanisms complicates the client and obscures the MODEL-DRIVEN DESIGN."
För den som inte har boken så kan citatet ovan även läsas i word-filens avsnitt om repositories:
http://domaindrivendesign.org/books/PatternSummariesUnderCreativeCommons.doc

/ Tomas

Medlem sedan feb. 200563 inlägg
#34

fredrikn skrev:

För att märka ord och vara märkvärdig ;) Så är inte ett "fake" Repository en mock utan en stub.

Det beror på vilka metoder i ett Repository som man använder och vad man vill testa.

Om man använder en Test Double för att i ett integrationstest verifiera att en uppdaterande repository-metod blir anropad så är det en Mock Object implementation som verifierar "indirect output".
Man kan t.ex. implementera ett test för en metod i Application/Service layer som efter valideringar kommer att verifiera att t.ex. en Add-metod på repositoriet anropades eller inte anropades (beroende på valideringsresultatet).

När det däremot gäller att implementera Test Doubles för query-metoder på ett repository så kan syftet ofta vara att man faktiskt vill hämta data (snarare än att bara verifiera att query-metoden blev anropad) och i så fall är det en Test Stub, men det kan också finnas varianter av tester där man även vill verifiera att en query-metod blir anropad i vissa scenarion men inte i andra (dvs man kan även vilja verifiera indirekt output med ett Mock Object för query-metoder).

Gränsen mellan olika typer av Test Doubles är f.ö. inte alldeles glasklar, vilket illustreras i bilden "Figure 2" i MSDN-artikeln "Exploring The Continuum Of Test Doubles"

/ Tomas

Medlem sedan okt. 200850 inlägg
#35

Tomas för att göra min mening "Så är inte ett "fake" Repository en mock utan en stub. " mer tydlig så kommer en kort förklaring:

Om man skapar en "fake" implementation utifrån ett Repository interface för att ha metoder som returnerar fake data istället för att gå mot en datakälla och kör mot denna "fake" istället för sin "oringal" Repos, så är det en "Fake" men lutar väldigt mycket mot "Stub" och är inte en Mock, som många gärna vill tro. Som du skriver i ditt inlägg så används termen Test Double som en generisk term för låtsas object som ersätter andra object vid testning och det finns olika varianter av namn på dessa test objects, tex, fake, stubs, mock och dummy.

En annan bra artikel inom detta ämne är Martin Fowlers artikel "Mocks aren't Stubs": http://martinfowler.com/articles/mocksArentStubs.html

Där skriver han tex:

Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.

Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example).

Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it 'sent', or maybe only how many messages it 'sent'.

Mocks are what we are talking about here: objects pre-programmed with expectations which form a specification of the calls they are expected to receive.

Medlem sedan aug. 20003 575 inlägg
#36

fredrikn skrev:

Om IList, andra använder den för sina egna implementationer av listor.

Till 95% så kommer du inte behöva byta ut dina Repo, gör du det så kan du enkelt ändra dom du redan har, eller använda dig av Visitor Pattern. Eller tex använda ADO.Net EF, som kan gå mot olika datakällor, eller ditt egna alternativ. Det jag försöker säga är att det spelar ingen roll om "orginal" Repos ligger i den assembly där dess interface ligger, eller separat, går lika lätt att byta ut. Alla Repos vi skapar kommer att ändå behöva ha en ref till våra entiteter, sedan kör vi med Factories som "gömmer" Repositories faktiska existens.

När det kommer till Stubbar ("Fake" Repo) Repos, så lägger jag dom till mitt TestPorjekt för jag tycker dom hör hemma där. En snabb bild på hur jag brukar skapa mina projekt:

Nsquared2.<SubSystem>.DomainLayer
               Entities
               Repositories
                     <CustomerRepsitory>
                        Interfaces
                                <ICustomerRepository>

Nsquared2.<SubSystem>.TestProject
               Stubs
                      StubCustomerRepository

Samla mycket med försiktighet i en assembly och kör med namspace för att minska på assemblies. OBS! Tidigare separerade jag Repos till en egen Assembly, men efter att jag har jobbat i projekt med massor av class libs i en solution, så gick jag över till mindre assemblies och mer namespace istället, för att slippa massor av dependency referencer. Nu kör jag med denna modellen även för små projekt, mer av vana och att jag trivs med den.

Skulle min Infra förändras i Repos, så är det stor sannolikhet att hela min app kommer alltid köra med den nya. Då kan jag lika gärna uppdatera mina Repos jag har, går snabbare än att skriva nya. Skulle det vara så att jag vill ha kvar mina orginal Repos, så skapar jag en ny assembly som refererar till Nsquared2.DomainLayer.dll vilket den måste för att komma åt entiteterna. Min Repos-factory tar hand skapandet av rätt Repo, tex:

var customerRepository = RepositoryFactory.Create<ICustomerRepository>();

Sedan i tex XML står det:

<object id="ICustomerRepository" type="Nsquared2.<SubSystem>.Repositories.CustomerRepository, Nsquared2.<SubSystem>.DomainLayer.dll"/>

Där det går att konfigurera vilken instans min ICustomerRepository är mappad mot.

Men återigen, det är en smak sak, det finns inget rätt eller fel med det du gör eller det jag gör. Mitt är ett arv från stora apps och jag föredrar så få assemblies som möjligt, för jag gillar inte att ha massa dependency ref och .dller som ska deployas, utan så få som möjligt, och sedan pga det Patrick Smacchia säger om assemblys vs namespace. Jag tror inte vi kommer någon vart längre.. Du har hittat en modell som du tycker funkar bra för dig, jag har hittat ett en funkar bra för mig, det är huvudsaken. Men självklart är diskutionen intresant varför olika väljer att göra på olika sätt.

Jag håller med dig i det mesta men det är några småsaker jag inte håller med om.

1. Jag hade inte kallat mitt namespace för DomainLayer utan lagt det Entiteterna direkt i rooten eftersom jag inte vill att alla projekt eller namespaces skall ha entiteterna utan att behöva använda using för att kunna använda entiteterna i exemplet under så får WebClient kännedom om alla entiteter utan att behöva lägga till en referens i klassen. Dock så hade jag kunnat kalla Assemblyn för DomainLayer.

[Projekt]
[Projekt].WebClient

2. Jag undrar lite varför du väljer att ha ännu ett namespace under Repositories.Interfaces okej att man får det mer uppdelat men jag tycker det känns helt onödigt att man skall behöva gräva mer i namespacet för att få tag på interfacen än implemmentationerna. Microsoft har t.ex. inte
System.Collections.Generics.Interfaces, här tycker jag nog att man skall tänka samma sak, lägg interfacen med implemmentationerna (om nu implemmentationerna ligger i Core (som jag gillar) eller DomainLayer assemblyn. Microsoft's guidelines säger även att man inte skall dela upp det för mycket i namespaces eller ha namespaces med alldeles för få klasser/interfaces.

Orsaken till att jag skriver att implemmentationen av IRepository kanske skall ligga utanför assemblyn är för att man kanske inte vill ha de beroenden i Core/DomainLayer projektet som lagringen innebär. Det slipper man ju om man använder Factories eller Microsoft Unity som jag verkligen har börjat gilla.

Men känner man att detta inte är en sådan big deal så är det bara att tuta och köra med implemmentationerna inuti Core/Domainlayer assemblien, jag tycker också som du när det gäller Stubbarna in i testprojekten :).

Medlem sedan okt. 200850 inlägg
#37

@Nickemannen:

"Jag hade inte kallat mitt namespace för DomainLayer".

Eftesom i DDD så har vi olika lager, Service/Application Layer, Domain Layer och Infrastructure Layer, så brukar jag anvdända mig av dom namnen i mina assemblies, mest pga tydlighet. Tex så ser mina pojekt ut så här:

WebClient
DomainLayer
InfrastructureLayer
ApplicationLayer

Ibland utelämnar jag "Layer" från namnen.. och detta är av slump och inte av något spec skäl.. utan bara blir så ;) Men tanken är att jag lägger alla interface och klasser som berör spesifika layers i en om samma assembly. Detta för att enkelt kunna hitta mina entiter och Repos etc.

Om Repositories.Interfaces.

Om jag har ett litet projekt så bryr jag mig inte om att lägga alla interface i en egen assembly.. Men om jag har flera Repositories, vilket kan uppstår pga av ett Repo per context, så vill jag gärna lägga undan interfacen och samla alla på ett ställe, mest för att inte plottra ner med både mängder och klasser samt interface. Så det är ett val jag gjort, får då bättre översikt över alla interface och kan lätt hitta dom.

Många separerar sina Repos och dess interface till egen Assembly, typ:

WebClient
DomainLayer
InfrastructureLayer
ApplicationLayer
Repositories

För att göra en tydlig seperation of concerns. Det finns ingen regel eller något definierat att Repositories ska ligga i Domain Layer. Men i de senare projektet så har jag även inkluderat mina Repos i DomainLayer assemblyn, mest för att undvika massor av assemblies och dependency ref (skrivit om detta i tidigare poster), sedan så är det kända DDD profiler som ofta lägger dom ihop med Domain Layer, så jag är en liten copy cat :P

Medlem sedan jan. 20022 440 inlägg
#38

fredrikn skrev:

sedan så är det kända DDD profiler som ofta lägger dom ihop med Domain Layer, så jag är en liten copy cat :P

Det är inget fel på det, gör så också. Man måste ju ha förebilder ;)

Medlem sedan aug. 20003 575 inlägg
#39

Dubbelpost

Medlem sedan aug. 20003 575 inlägg
#40

fredrikn skrev:

@Nickemannen:

"Jag hade inte kallat mitt namespace för DomainLayer".

Eftesom i DDD så har vi olika lager, Service/Application Layer, Domain Layer och Infrastructure Layer, så brukar jag anvdända mig av dom namnen i mina assemblies, mest pga tydlighet. Tex så ser mina pojekt ut så här:

WebClient
DomainLayer
InfrastructureLayer
ApplicationLayer

Ibland utelämnar jag "Layer" från namnen.. och detta är av slump och inte av något spec skäl.. utan bara blir så ;) Men tanken är att jag lägger alla interface och klasser som berör spesifika layers i en om samma assembly. Detta för att enkelt kunna hitta mina entiter och Repos etc.

Om Repositories.Interfaces.

Om jag har ett litet projekt så bryr jag mig inte om att lägga alla interface i en egen assembly.. Men om jag har flera Repositories, vilket kan uppstår pga av ett Repo per context, så vill jag gärna lägga undan interfacen och samla alla på ett ställe, mest för att inte plottra ner med både mängder och klasser samt interface. Så det är ett val jag gjort, får då bättre översikt över alla interface och kan lätt hitta dom.

Många separerar sina Repos och dess interface till egen Assembly, typ:

WebClient
DomainLayer
InfrastructureLayer
ApplicationLayer
Repositories

För att göra en tydlig seperation of concerns. Det finns ingen regel eller något definierat att Repositories ska ligga i Domain Layer. Men i de senare projektet så har jag även inkluderat mina Repos i DomainLayer assemblyn, mest för att undvika massor av assemblies och dependency ref (skrivit om detta i tidigare poster), sedan så är det kända DDD profiler som ofta lägger dom ihop med Domain Layer, så jag är en liten copy cat :P

Angående DomainLayer namespace
Okej, jag köper ditt argument, jag gjorde också så tidigare men så insåg jag att alla andra namespaces använder sig av det som ligger i DomainLayer namespace därför lägger jag dom alltid i namespacerooten för projektet. Däremot säger jag inte att det är fel att låta assemblyn heta DomainLayer.

Men jag ser nu att du snackar om assemblies och jag menade mer namespaces. Assembly och namespace måste ju inte ha samma heiraki :), enligt Framework Design Guidelines boken är det även ovanligt att de har det :).

Angående interface:
Även om du har dina interface i en annan assembly så kan dom lika gärna ligga under [ProjektNamn].Repositories man kan ju låta flera assemblies använda samma namespace, men jag håller med dig det blir bättre uppdelat som du säger om du låter dom ligga i ett eget subnamespace men jag personligen tycker att det är onödigt utan låter dom hellre ligga direkt i [ProjektNamn].Repositories.

Angående bryta ut repositories implemmentationerna.
Håller med dig i de flesta projekt behövs inte det, jag ville bara poängtera när man kanske vill bryta ut dom :).

277 ms totalt · 4 externa anrop · v20260731065814-full.a51de22e
122 ms — deklarationer (db)
0 ms — hämta statistik (cache)
150 ms — hämta tråd, inlägg och bilagor (db)
125 ms — ändringar (db)