5

I'm attempting to create a small demo web application that is loosely coupled, highly testable, has nice clean code, etc. In short, I'm experimenting with doing things the right way. ;)

I currently have three projects in my Wolfie solution:

  • Wolfie.Core - contains business entities
  • Wolfie.Data - contains data access code, references Core.
  • Wolfie.Web - will be a nancy web site.

As it stands the Core knows nothing about any of the other projects. Data has to reference Core as Core has the types that Data will return. So at this point I find myself realising that Web will need to reference both Core and Data to be able to work as the entity type is in Core and the database calls are in Data.

All of the repository classes in Data have interfaces so that the repositories can be mocked out for testing.

I don't think I want to put any database specific code or references into Core, and I want to keep my entity business rules out of Data.

Is it correct to reference both projects from Web? or would another project between Web and the others be required so that Web only references the one place and isn't then responsible for calling Data methods etc.

What I'm aiming for is an architecture where my core application is independent of both the Data and Web layers.

I hope I've made some sense and I look forward to some helpful replies.

DavidGouge
  • 4,583
  • 6
  • 34
  • 46
  • It sounds like you've already got the answers. You are absolutely correct to isolate the business core logic from technology implementations. It almost sounds as if you're familiar with the Onion architecture described in Jeffrey Palermo's blog and used in the ASP.Net MVC In Action book series by him and others. – StarTrekRedneck Feb 09 '12 at 15:07

5 Answers5

9

I think you are striving for a reasonable goal which will create a solid application architecture. I have personally worked on an enterprise web application that used a similar architecture to the one you are describing, and it worked very well for that application.

In that application, the domain business logic was isolated in its own "Domain" assembly (your "Core" assembly) and referenced nothing outside of the .NET framework. In order to connect to external business components, such as the database and other web services, there was an "Infrastructure" assembly (your "Data" assembly). The key for that application is that each implementation in the Infrastructure assembly was interfaced in the Domain assembly, and returned Domain assembly objects (just as you describe). This, of course, necessitated a reference from Infrastructure to Domain.

To tie it all together, a "WebApp" assembly (your "Web" assembly) referenced both the Domain and Infrastructure assemblies and used an IoC container to resolve all of the application dependencies. When requests came in to the WebApp, it would resolve the appropriate "Domain" contract to serve the request which would start the IoC resolution call chain. Outside of registering the Infrastructure implementations, the WebApp assembly does not care that the Infrastructure assembly exists.

The reason this worked so well for that application is that it achieved the goals you stated in your question:

  • There are no external references in the business domain.
  • The business domain application is completely independent of both the web front-end and the database back-end.

This lends a lot of testability to your business domain, and a lot of flexibility to how front-ends interface with your domain. In fact, that flexibility is what makes it so testable. That flexibility has other benefits as well. For instance, with your business domain that isolated, you could easily code up a new CLI or WPF front-end that hooked into a file-system back-end without changing a scrap of business domain code if you wanted to deploy and run your client locally on a machine.

Though that example may seem slightly far-fetched, in the case of the application I was referencing above, the development team is contemplating building a Web Services front-end to complement the already existing MVC website front-end. This is only a realistic possibility for that team because they setup that application like you are describing.

Drew Spickes
  • 234
  • 2
  • 7
  • Nice story! (and maintainability increases even more if you have service layer in place; UI and web/data services implementation becomes pretty easy). – mikalai Feb 09 '12 at 16:52
4

I don't think there's anything inherently wrong with having those two references.

But, I would consider what "Core" is. Is this your domain logic? If so, I'm not sure I would have a situation where your data access assembly knows about your domain logic and your GUI layer knows about both.

If "Core" is domain logic, you can put classes in it (e.g. IRepository Pattern) that know about the "Data" assembly. Your web assembly would then know about "Core", but not "Data".

To facilitate this arrangement, you can create a fourth assembly called "DataTransfer" and define some objects (or, better yet, interfaces) that "Core", "Data" and "Web" all know about and use to communicate with one another. As a matter of fact, if you really want to get decoupled, structuring things with the "Types" assembly containing interfaces can actually make it so that none of your original three assemblies know about each other.

But then, if "Core" is really a library rather than your domain logic, it probably makes sense to go with your current structure and perhaps later revisit it if you add enough functionality to start tacking on more assemblies/layers.

Edit: To help visualize what I mean, I'm talking about a layered situation:

Web
 | 
 v
Core
 |
 v
DataAccess

rather than:

Web
 |  \
 v   \
Core  |
 ^    |
 |    v
DataAccess

You have three references when you could make it work with two. Extra/unnecessary coupling early leads to headaches later.

Erik Dietrich
  • 6,080
  • 6
  • 26
  • 37
  • Thanks for the advice. So if Core is now referencing Data rather than the other way round, what types would Data return / receive as it wouldn't know about the types in Core? Would I need some sort of intermediate types (I guess that's what you mean by the DataTransfer assembly). – DavidGouge Feb 09 '12 at 10:08
  • Yes, exactly. The "DataTransfer" assembly would have POCOs or, better yet, interfaces that Core/Data/Web would know about and could use to communicate via parameters and return values. – Erik Dietrich Feb 09 '12 at 15:06
  • 2
    It is a mistake for the root business logic (Core/Domain/etc.) to reference any technological implementation, such as a database, file system, etc. The business concepts are to be stand alone. The data access layer is an *implementation* of a business concept, e.g., an IOrderRepository or even an IRetrieveOrderProcess. That is why Core should not reference DataAccess. – StarTrekRedneck Feb 09 '12 at 15:11
  • The data access layer is concerned with particulars of persistence mechanics. It is not at all "an implementation of a business concept". Here's a link that will probably help clarify it for you better than I can in 500 characters of comment: http://msdn.microsoft.com/en-us/library/ee658127.aspx – Erik Dietrich Feb 09 '12 at 15:49
  • Also, to clarify, I don't advocate that domain objects know about persistence objects at all. I'm advocating that objects (repositories) within domain have a reference to data access layer interfaces that they use to get data transfer objects and populate/create rich domain objects. The rich domain objects know nothing about other layers of the application and are thus completely independent and testable. – Erik Dietrich Feb 09 '12 at 15:56
  • Jeffrey Palermo's blog [article](http://jeffreypalermo.com/blog/the-onion-architecture-part-1/) which StarTrekRedneck referenced above provides an excellent rebuttal to the Microsoft article you referenced. @DavidGouge specifically mentioned he was looking for a _loosely coupled_ architecture. A direct reference from the business domain assembly to the data access assembly is a point of _tight coupling_, which the Onion architecture is attempting to remedy. – Drew Spickes Feb 09 '12 at 16:07
  • Palermo's article also says "This architecture is not appropriate for small websites. It is appropriate for long-lived business applications as well as applications with complex behavior." But, I digress -- your point about coupling is well taken. What I was trying to do was offer a way to go from more coupling to less coupling, rather than ramping all the way up to "ideal" in one fell (and possibly hard to grasp) swoop. Layer or onion architectures both allow indirection between domain and other assemblies where there are no direct references... – Erik Dietrich Feb 09 '12 at 16:29
  • In the architecture I'm proposing, the next logical decoupling proposition would be to create a "Types" assembly that defines all wireup APIs as interfaces only. Then, all assemblies (data access, domain, web, etc) reference only that assembly and know nothing about each other. IoC framework then wires things up. This way, it isn't only domain that is know-nothing an decoupled -- it's all of your assemblies. – Erik Dietrich Feb 09 '12 at 16:31
  • I agree that complexity of the site is a major factor. And since the goal is loose coupling and testability, constructor-based dependency injection seems ideal here. I think direct chain referencing like in your diagram is too simplistic to pull this off because there's no hub from which an IoC container can operate. It may also imply (to me at least) that we're "newing" stuff up, which is a unit test killer. – StarTrekRedneck Feb 09 '12 at 19:52
  • It can be done by having all service functionality in each layer accessed only through interfaces by the layer above it. IOC then comes along and wires up specific instances. (I've done this successfully before with 100% code coverage). But, having a separate assembly containing all the interfaces with none of the assemblies knowing about each other does wind up being cleaner than what I diagrammed, so you have a valid point. – Erik Dietrich Feb 09 '12 at 20:00
  • 3
    IMHO, he should start with a WebApp->Core reference and a WebApp->Infrastructure reference which is a collection of implementations of business concepts that require external dependencies (not just data access). IoC bootstrapping is called from the startup of the WebApp. That's a minimal solution that brings no restrictions. If he wants to separate out the DataAccess into it's own assembly with DTOs and mapping, he can do so easily and handle mapping in the infrastructure assembly which will ref that DataAccess layer, but he doesn't have to. The Core will remain standalone and very testable. – StarTrekRedneck Feb 09 '12 at 20:07
  • Thanks for your answers and comments. I'm pretty sure I've got it in my head now about Dependency Inversion, IoC etc. It's that reference from WebApp->Infrastructure that bothers me for some reason. I've put my further thoughts in a separate question: http://stackoverflow.com/questions/9225978/reference-vs-dependency – DavidGouge Feb 10 '12 at 10:07
3

You're making a lot of sense and your reasoning is very sound. It's a very common, reasonable and pragmatic design to have Web layer referencing both Core and Data. You're not creating any circular references and keeping it pretty clean.

Yuriy Zubarev
  • 2,821
  • 18
  • 24
  • 2
    No, it's not. The UI layer should not be aware of the data layer implementation. However, the UI can be aware of the data layer abstraction (in form of repository interfaces). – jgauffin Feb 08 '12 at 21:19
1

The architecture is 1. data 2. business logic 3. client. Data is data. Business logic works on the data. Client connects to the business logic like data->business logic->client. Client doesn't connect to the data directly. 3 layers. data logic client.

iefpw
  • 6,816
  • 15
  • 55
  • 79
1

I think it makes sense. As long as you separate Web from business layer, it allows you to test business logic easier as well as expose business layer via the other ways (e.g., Web Services) as so on.

kimsk
  • 2,221
  • 22
  • 23