Question

Please note that this code is a code example of how I usually write my code but I have just removed code that would remove focus from my question. I look forward to listen.

I can understand that I need atleast 10 reps. before I can post images and my images illustrates my question... So please follow this link to see my original question at codereview.stackexchange.com - https://codereview.stackexchange.com/questions/44237/domain-modelling-with-repository

I've been struggling with some architectural problems which I'm having a hard time figuring out myself.

Im trying to construct a basic structure of a project with the domain model and repository pattern.

It's easy enough to construct the POCO classes and repository when I want to implement some Business logic and different kinds of UI (i.ex. WinForms and MVC) I feel that I miss something because I feel that my code is tightly coupled and I always have to reffer to the POCO classes when ever I need to get an object and show it.

I start out by constructing the following projects in C#(vs2012):

Model

DAL

BL

TestConsole

Here is an example of my Model a model Class:

namespace Panda.Model
{
    public class Person : IEntity
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public Person()
        {

        }
        public Person(string name)
        {
            this.Name = name;
        }
    }
}

Here is an example code from my Persons class in my BL project:

using Panda.DAL.Repositories;
using Panda.DAL.Contexts;
using Panda.Model;
namespace Panda.BL
{
    public class Logic
    {
        private readonly IRepository<Person> _personRep;
        public Logic()
        {
            _personRep = new Repository<Person>(new GenericContext());
        }

        public LinkedList<Person> ListOfPersons()
        {
            LinkedList<Person> persons = new LinkedList<Person>();
            persons.AddFirst(new Person("Nicklas"));
            persons.AddFirst(new Person("Martin"));
            persons.AddFirst( new Person("Kresten"));
            return persons;
        }
    }

My DAL project consists of generic Repositories that takes a Class of type IEntity:

public class Repository<T> : IRepository<T> where T : class, IEntity
    {
        /// <summary>
        /// The Generic Repository class that can use all Model classes when istantiating it.
        /// It holds all the generic methods for insert, select, delete and update.
        /// </summary>
        internal DbSet<T> DbSet;
        internal GenericContext Context;
        public Repository(GenericContext context)
        {
            this.Context = context;
            DbSet = context.Set<T>();
        }

My code for the program.cs file in the Console app looks like this:

using Panda.BL;

namespace Panda.TestConsole
{
    public class Program
    {
        static void Main(string[] args)
        {
            Logic lol = new Logic();

            foreach (var item in lol.ListOfPersons())
            {
                Console.WriteLine(item.Name);
            }
        }
    }
}

Problem is that I don't know how to decouple my Model and DAL further from my UI project (Console etc..) Everytime I want to get i.ex. a Person object I have to of course to reference to my Model project from my console project when ever I want to use a method from the BL project.

My understanding of the whole DDD and 3 tier pattern is that you should be able only talk (reference) to the BL when ever you want to add a new UI project (i.ex. Console, WebForms or MVC) But now I always have to reference to both Models and BL when ever I want to use a method in the BL project.

Right now I feel like have alot of depencies which coupling things to tightly..

I really look forward to hear your thoughts on this which has been puzzling me for a while.

Thanks in advance

Was it helpful?

Solution

Right now I am also writing some 3-tier application as a training for my skills. I also created a project structure as you: BLL, DAL, Model, UI (an MVC project) and Tests layer. From my experience I know that your e.g. main application (in my case the UI layer with MVC project) should have reference to BLL and Model only! You should not add reference to DAL. The BLL should make use of Model and DAL. In the end the DAL should only reference to the Model. That's it.

BTW, you should avoid this:

public class Logic {
    private readonly IRepository<Person> _personRep;
    public Logic()
    {
        _personRep = new Repository<Person>(new GenericContext());
    }
}

Instead make use of dependency injection:

public class Logic {
    private readonly IRepository<Person> _personRep;
    public Logic(IRepository<Person> personRep)
    {
        _personRep = personRep;
    }
}

OTHER TIPS

To decouple UI I use a DataTransferObject pattern so that my services layer or BAL is the only layer then using Domain and Repository.

DTO's are just poco that only contain the information for the object your are transmitting and that is it, nothing more.

To map data from domain object to dto I then use AutoMapper (a god send)

Your UI layer whatever it may be will then only ever deal with dto's relevant to the work being done at that point in time. Whether it be from a Web Services layer or the service library itself.

Domain Layer

Altered your sample above to show how it works a bit better and added concurrency field to show how to handle that also with the dto's and when its needed.

public class Person : IEntity
{
    [key]
    public int Id { get; set; }

    [Required]
    [StringLength(20)]
    public string FirstName { get; set; }

    [StringLength(50)]
    public string Surname { get; set; }

    [Timestamp]
    public byte[] RowVersion { get; set; }

    public Person() { }

    public Person(string firstName, string surname)
    {
        FirstName = firstName;
        Surname = surname;
    }
}

DTO Layer

In one namespace for easier viewing, i usually separate every dto class into its own file as per normal, and also create folders to store relevant dto in. ie. CreatePersonDto will go in the Create folder along with all other Create Dto's, ListPersonDto in the List folder, QueryPersonDto in the Query fodler within the project, adds extra using references to your class files but that's nothing.

namespace Panda.DataTransferObjects
{
    public class PersonDto
    {
        public int? Id { get; set; }

        public string FirstName { get; set; }

        public string Surname { get; set; }

        public byte[] RowVersion { get; set; }
    }

    public class CreatePersonDto
    {
        public string FirstName { get; set; }

        public string Surname { get; set; }
    }


    public class EditPersonDto
    {
        public int Id { get; set; }

        public string FirstName { get; set; }

        public string Surname { get; set; }

        public byte[] RowVersion { get; set; }


        // user context info, i would usually use a separate ServiceContextDto to do
        // this, if you need to store whom changed what and when, and how etc 
        // ie. log other information of whats going on and by whom.
        // Needed in Create and Edit DTO's only
        public string ChangedBy { get; set; }
    }

    public class ListPersonDto
    {
        public string Name { get; set; }
    }

    public class QueryPersonDto
    {
        public int? Id { get; set; }

        public string FirstName { get; set; }

        public string Surname { get; set; }
    }

}

BAL or Services Layer

Add to your stuff above

Firstly a Create person method. You UI layer will create a dto setup the info and call the create method below. Create dto's do not need to contain Id, timestamp (rowversion) for concurrency etc, as none of that information is needed when creating a new object all you need is the members of the object that can be added by the repo. In this example FirstName and Surname only. Other data like user context data etc could also be passed through within these objects, but you don't need anything else.

        public int CreatePerson(CreatePersonDto dto)
        {
            //checks to ensure dto is valid

            var instance = new Person(dto.FirstName, dto.Surname);                                

            // do your stuff to persist your instance of person. ie. save it

            return instance.Id;
        }

Secondly a Get of the Person instance. You pass it the id of the person instance your after and it retrieves it and returns a PersonDto. Firstly you need to obtain your Person object from the persistence layer via your repository, then you need to convert that object to the Dto for return to the client. For the mapping i use AutoMapper, it will help enormously with this type of pattern, your doing a lot of mapping from one object to another and that is what it is for.

        public PersonDto Get(int id) {
             Person instance = // repo stuff to get person from store/db

             //Manual way to map data from one object to the other.
             var personDto = new PersonDto();
             personDto.Id = instance.Id;
             personDto.FirstName = instance.firstName;
             personDto.Surname = instance.Surname;
             personDto.RowVersion = instance.RowVersion;

             return personDto;


             // As mentioned I use AutoMapper for this, so the above becomes a 1 liner.
             // **Beware** there is some configuration for this to work in this case you
             // would have the following in a separate automapper config class.
             // AutoMapper.CreateMap<Person, PersonDto>();
             // Using AutoMapper all the above 6 lines done for you in this 1.
             return Mapper.Map<Person, PersonDto>(instance);
        }

ListPersonDto

As previously stated use AutoMapper for this and things like object conversion in a query becomes painless.

        public IEnumerable<ListPersonDto> ListOfPersons(QueryPersonDto dto = null)
        {
            // check dto and setup and querying needed
            // i wont go into that

            // Using link object mapping from the Person to ListPersonDto is even easier
            var listOfPersons = _personRep.Where(p => p.Surname == dto.Surname).Select(Mapper.Map<Person, ListPersonDto>).ToList();

            return listOfPersons;
        }

Just for completeness the automapper signature for the above would look something like the following, considering ListPersonDto only contains name.

AutoMapper.Mapper.CreateMap<Person, ListPersonDto>()
    .ForMember(dest => dest.Name, opt => opt.ResolveUsing(src => { return string.Format("{0} {1}", src.FirstName, src.Surname); } ))

So your app will then need to only see the BAL & dto layer.

public class Program
{
    static void Main(string[] args)
    {
        Logic lol = new Logic();

        CreatePersonDto dto = new CreatePersonDto { FirstName = "Joe", Surname = "Bloggs" };
        var newPersonId = lol.Create(dto);

        foreach (var item in lol.ListOfPersons())
        {
            Console.WriteLine(item.Name);
        }

        //or to narrow down list of people
        QueryPersonDto queryDto = new QueryPersonDto { Surname = "Bloggs" }

        foreach (var item in lol.ListOfPersons(queryDto))
        {
            Console.WriteLine(item.Name);
        }
    }
}

It adds extra work, but unfortunately there is no easy way to do it, and using a pattern like above will help separate things and make it easier to track down issues. Your dto should only ever have whats necessary for the operation, so it make it easier to see where things are left out or not included. AutoMapper is a must have for this scenario, it makes life much easier, and reduces your typing, there are plenty good examples of using automapper around.

Hope this helps in getting closer to a solution.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top