Reputation:
The team I work with (3 people) are currently discussing the architecture of a new medium size web application and as you can imagine there are contrasting opinions within the team about how the application should be designed. What I'm trying to do is post some of these ideas with the advantages /disadvantages to each approach that we know of so the SO community can help us go down the best route and/or achieve a compromise.
Architecture A:
Repository layer:
Calls to Entity Framework
Service / Business Layer:
Presentation Layer (MVC):
Architecture B:
Service / Business Layer:
2. Presentation Layer (MVC):
Some assumptions you can make:
Sorry, if this appears contrary, but my preferred architecture is actually B for a number of reasons.
I've worked on projects that are abstracted to the nth degree and it does take a lot longer to make changes to each of the layers, rebuild in VS, revise the tests / mock methods / etc and it offers not much return for projects that are essentially simple CRUD applications
The business don't care if the app has the cleanest architecture in the world, they just want to ship the product quickly without too many bugs
For the vast majority of methods (in all layers) a simple functional test is all that's required. If the method GetProductById doesn't work, fix it! In my opinion a unit test method containing more lines of code than the method being tested just isn't required for simple methods.
I want to be able to right click a method, choose 'Go to definition' and see that actual method source! I don't want to see the interface definition and then have to lookup the actual method elsewhere. Sure if everything uses interfaces then swapping to a different database should* be a breeze but realistically that just doesn't happen nor is it likely to happen for this project.
In summary, I'm not against interfaces or unit tests however where there seems to be a proliferation of either that brings little to no benefit I can't see the point.
Anyone have any suggestions/ advice?
Upvotes: 0
Views: 380
Reputation: 1714
I agree with what jgauffin says, but I would like to add something, so I'm using his template for uniformity:
Architecture A
1) Repository layer: Calls to Entity Framework
As jgauffin very well said, abstracting the O/RM is a good way to make the BL testable, indeed I would go as far as to say that it is the best way to unit test your BL, which is in general important. Remember, though, that there are advocates who say that you shouldn't hide the O/RM behind a repository for several reasons. From that blog post:
The problem with this pattern is that it totally ignores the existence of mature persistence technologies, such as NHibernate. NHibernate already provides an illusion of in memory access, in fact, that is its sole reason of existing. Declarative queries, check. OO view on the persistence store, check. One way dependency between the domain and the data store, check.
So, what do I gain by using the repository pattern when I already have NHibernate (or similar, most OR/M have matching capabilities by now)?
The debate is still open, so it's your call. With NHibernate you could still unit test the BL mocking the ISession, I don't know how it works with EF: alternatively you could test your BL with integration tests hitting an in-memory database (such as SQLite).
2) Service / Business Layer: - Each 'Manager' to have an interface - Each 'Manager' to have 2 implementations (1 mock / 1 actual) - Methods in Manager classes to call Repository and then apply business logic - Each method to be unit tested
jgauffin says that you shouldn't put mocking in your BL, and he's right, but as long as your mocks are in an extra project I see only advantages during integration testing, especially if you are using WCF services: then you could have different configurations (or no configuration at all with discovery, just scopes) for mock and real services and test your services against several configurations.
3) Presentation Layer (MVC): - Managers to be created via some sort of Service Locator pattern - Each controller to have unit tests written against it
There are people claiming that Service Locator is an antipattern and the argument is pretty convincing, even if sometimes Service Locator odes look like the lesser evil. MVC offers more elegant solutions for Dependency Injection compared to WebForms, so you should definitely look into it.
Architecture B
1) Service / Business Layer: - Contains only concrete 'Manager' classes - Methods call Entity framework directly (eg we're treating EF as the repo) - Interfaces may be used but only where there is a need for different implementations - Unit tests as and when when required
As previously said, this could theoretically work easily with integration tests using an in-memory database, if it is fast enough for you.
2) Presentation Layer (MVC): - Manager classes to be created directly - Unit test only code that is fragile or of significant complexity
The only advantage that I can see not using an IoC Container is to have one less moving part; for testing purposes I would still use DI though, so do you really want to inject your dependencies manually, Poor Man's DI style, for a medium sized project?
Summary
To keep it short, there is always a middle ground, and we can't really make that decision for you. Being it a medium sized project, I would tend toward something similar to Architecture A; the only exception is if it were a short lived project, where you have a determinate date where your app is obsolete and is going to be taken down (think an application for a particular non-recurring event).
Generally speaking, if it is not a demo project, I seldom skip a good abstraction. As jgauffin implicitly said, think of abstractions not as a mean of swapping implementations, but as a way to manage the complexity of your application.
Upvotes: 0
Reputation: 101130
Architecture A
1) Repository layer: Calls to Entity Framework
Abstracting away the OR/M is a good way to make the dependning classes (SL/BL) testable.
2) Service / Business Layer: - Each 'Manager' to have an interface - Each 'Manager' to have 2 implementations (1 mock / 1 actual) - Methods in Manager classes to call Repository and then apply business logic - Each method to be unit tested
Don't put mocks in the business layer. They do not belong there. Use a mocking framework instead, those also allows you to verify that the classes call the correct methods in the dependencies.
3) Presentation Layer (MVC): - Managers to be created via some sort of Service Locator pattern - Each controller to have unit tests written against it
Service location hides dependencies. Use Dependency Injection instead (easiest approach today is to use an Inversion Of control container)
Architecture B
1) Service / Business Layer: - Contains only concrete 'Manager' classes - Methods call Entity framework directly (eg we're treating EF as the repo) - Interfaces may be used but only where there is a need for different implementations - Unit tests as and when when required
Sure. You can mock EF4, but it's a lot harder to verify that all calls are correct. Unit tests as and when when required
. That should always be the case. Why test something that don't need testing. A company policy telling when and what to test should always exist.
2) Presentation Layer (MVC): - Manager classes to be created directly - Unit test only code that is fragile or of significant complexity
Creating classes directly makes scoping hard and how do you create complex object graphs? It invites you to make the manager classes god classes or to create high coupling between the classes.
Questions
1) I've worked on projects that are abstracted to the nth degree and it does take a lot longer to make changes to each of the layers, rebuild in VS, revise the tests / mock methods / etc and it offers not much return for projects that are essentially simple CRUD applications
Very few applications are CRUD. Crud applications do not validate the model more than checking that the syntax is correct. Those applications will contain a lot of data which is invalid from the business perspective (but valid as correctly formatted)
2) The business don't care if the app has the cleanest architecture in the world, they just want to ship the product quickly without too many bugs
They do care a few years from now when the maintanence cost skyrocket.
3) For the vast majority of methods (in all layers) a simple functional test is all that's required. If the method GetProductById doesn't work, fix it!
What do you do six months from now when you introduce a new change/feature? Force the users to test everything that might be affected by your change? Since you can*t now if GetProductById
doesn't work until you test it.
In my opinion a unit test method containing more lines of code than the method being tested just isn't required for simple methods.
imho that method needs to be refactored and breakin down into smaller methods. Methods that are hard to test do most likely smell.
4) I want to be able to right click a method, choose 'Go to definition' and see that actual method source! I don't want to see the interface definition and then have to lookup the actual method elsewhere.
This one scares me. Are you willing to sacrifice code quality just because your IDE doesn't help you? Buy resharper and you get "Goto implementation". Resharper got a lot of software quality checks, it's well worth the money.
Sure if everything uses interfaces then swapping to a different database should* be a breeze but realistically that just doesn't happen nor is it likely to happen for this project.
That doesn't happen in any project and is not the reason to use interfaces. Well defined interfaces makes it a lot easier to refactor code and follow the SOLID principles.
Upvotes: 1
Reputation: 6199
It seems quite clear to me according to the business needs that you should just stick with writing the code in the simplest fashion and I would skip all the TDD stuff and use that dedicated QA guy instead and skip formal interface declarations except where they make sense.
Upvotes: 0