Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IOC and interfaces

I have a project structure like so :-

CentralRepository.BL
CentralRepository.BO
CentralRepository.DataAccess
CentralRepository.Tests
CentralRepository.Webservices

and there is an awful lot of dependencies between these. I want to leverage unity to reduce the dependencies, so im going to create interfaces for my classes. My question is in which project should the interfaces reside in. My thoughts are they should be in the BO layer. Can someone give me some guidance on this please

like image 799
Richard Banks Avatar asked Oct 12 '11 15:10

Richard Banks


2 Answers

On a combinatorial level, you have three options:

  • Define interfaces in a separate library
  • Define interfaces together with their consumers
  • Define interfaces together with their implementers

However, the last option is a really bad idea because it tightly couples the interface to the implementer (or the other way around). Since the whole point of introducing an interface in the first place is to reduce coupling, nothing is gained by doing that.

Defining the interface together with the consumer is often sufficient, and personally I only take the extra step of defining the interface in a separate library when disparate consumers are in play (which is mostly tend to happen if you're shipping a public API).

like image 117
Mark Seemann Avatar answered Oct 21 '22 18:10

Mark Seemann


BO is essentially your domain objects, or at least that is my assumption. In general, unless you are using a pattern like ActiveRecord, they are state objects only. An interface, on the other hand, specifies behavior. Not a good concept, from many "best practices", to mix the behavior and state. Now I will likely ramble a bit, but I think the background may help.

Now, to the question of where interfaces should exist. There are a couple of choices.

  1. Stick the interfaces in the library they belong to.
  2. Create a separate contract library

The simpler is to stick them in the same library, but then your mocks rely on the library, as well as your tests. Not a huge deal, but it has a slight odor to it.

My normal method is to set up projects like this:

{company}.{program/project}.{concern (optional)}.{area}.{subarea (optional)}

The first two to three bits of the name are covered in yours by the word "CentralRepository". In my case it would be MyCompany.CentralRepository or MyCompany.MyProgram.CentralRepository, but naming convention is not the core part of this post.

The "area" portions are the thrust of this post, and I generally use the following.

  1. Set up a domain object library (your BO): CentralRepository.Domain.Models
  2. Set up a domain exception library: CentralRepository.Domain.Exceptions
  3. All/most other projects reference the above two, as they represent the state in the application. Certainly ALL business libraries use these objects. The persistance library(s) may have a different model and I may have a view model on the experience library(s).
  4. Set up the core library next: CentralRepository.Core (may have subareas?). this is where the business logic lays (the actual applciation, as persistence and experience changes should not affect core functionality).
  5. Set up a test library for core: CentralRepository.Core.Test.Unit.VS (I have Unit.VS to show these are unit tests, not integration tests with a unit test library, and I am using VS to indicate MSTest - others will have different naming).
  6. Create tests and then set up business functionality. As need, set up interfaces. Example
  7. Need data from a DAL, so an interface and mock are set up for data to use for Core tests. The name here would be something like CentralRepository.Persist.Contracts (may also use a subarea, if there are multiple types of persistence).

The core concept here is "Core as Application" rather than n-tier (they are compatible, but thinking of business logic only, as a paradigm, keeps you loosely coupled with persistence and experience).

Now, back to your question. The way I set up interfaces is based on the location of the "interfaced" classes. So, I would likely have:

CentralRepository.Core.Contracts CentralRepository.Experience.Service.Contracts CentralRepository.Persist.Service.Contracts CentralRepository.Persist.Data.Contracts

I am still working with this, but the core concept is my IoC and testing should both be considered and I should be able to isolate testing, which is better achieved if I can isolate the contracts (interfaces). Logical separation is fine (single library), but I don't generally head that way due to having at least a couple of green developers who find it difficult to see logical separation without physical separation. Your mileage may vary. :-0

Hope this rambling helps in some way.

like image 33
Gregory A Beamer Avatar answered Oct 21 '22 16:10

Gregory A Beamer