簡體   English   中英

使用3層模式的存儲庫進行領域建模

[英]Domain modelling with Repository in a 3-tier pattern

請注意,該代碼是我通常如何編寫代碼的代碼示例,但是我剛剛刪除了將使問題不再關注的代碼。 我期待着傾聽。

我知道我至少需要10次。 在我發布圖片和圖片說明我的問題之前,...請先通過此鏈接在codereview.stackexchange.com- https ://codereview.stackexchange.com/questions/44237/domain-modelling-with中查看我的原始問題-存儲庫

我一直在努力解決一些架構問題,這些問題很難弄清楚自己。

我試圖用領域模型和存儲庫模式構建項目的基本結構。

當我想要實現一些業務邏輯和不同類型的UI(例如WinForms和MVC)時,構造POCO類和存儲庫就很容易了,我覺得我錯過了一些東西,因為我覺得我的代碼緊密耦合,而且我總是每當我需要獲取一個對象並將其顯示時,就必須參考POCO類。

我首先在C#(vs2012)中構建以下項目:

模型

DAL

BL

測試控制台

這是我的Model模型類的示例:

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;
        }
    }
}

這是我的BL項目中我的Persons類的示例代碼:

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;
        }
    }

我的DAL項目由通用存儲庫組成,該存儲庫采用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>();
        }

我在控制台應用程序中的program.cs文件的代碼如下所示:

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);
            }
        }
    }
}

問題是我不知道如何將我的模型和DAL與UI項目(控制台等)進一步分離。每次我要獲取i.ex時,都不會這樣做。 當我想使用BL項目中的方法時,我當然必須從我的控制台項目中引用我的Model項目。

我對整個DDD和3層模式的理解是,當您要添加新的UI項目(例如,Console,WebForms或MVC)時,您應該只能與BL交談(引用),但是現在當我想在BL項目中使用方法時,要同時引用Models和BL。

現在,我感覺到有很多缺陷使事情緊密聯系在一起。

我真的很希望聽到您的想法,這已經困擾了我一段時間。

提前致謝

現在,我還在編寫一些3層應用程序,以培訓我的技能。 我還為您創建了一個項目結構:BLL,DAL,模型,UI(一個MVC項目)和測試層。 根據我的經驗,我知道您的主應用程序(在我的情況下是帶有MVC項目的UI層)應該僅引用BLL和Model! 您不應添加對DAL的引用。 BLL應該使用Model和DAL。 最后,DAL應該僅引用模型。 而已。

順便說一句,您應該避免這種情況:

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

而是利用依賴注入:

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

為了解耦UI,我使用DataTransferObject模式,以便服務層或BAL是僅使用Domain和Repository的唯一層。

DTO只是poco,僅包含要傳輸的對象的信息,僅此而已。

要將數據從域對象映射到dto,然后使用AutoMapper(上帝發送)

您的UI層無論可能是什么,都只會處理與該時間點正在執行的工作相關的dto。 無論是來自Web服務層還是來自服務庫本身。

域層

更改了上面的示例以顯示它的工作原理,並增加了並發字段以顯示如何使用dto以及何時需要它。

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層

為了方便查看,在一個命名空間中,我通常按照正常方式將每個dto類分成自己的文件,並創建文件夾以存儲相關的dto。 CreatePersonDto將與所有其他Create Dto一起放入Create文件夾中,List文件夾中的ListPersonDto,項目中Query fodler中的QueryPersonDto,為類文件添加了額外的使用引用,但僅此而已。

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或服務層

添加到上面的東西

首先是創建人方法。 您的UI層將創建一個dto來設置信息並調用下面的create方法。 創建dto不需要包含Id,時間戳(行版本)以進行並發等,因為在創建新對象時不需要任何信息,您所需要的只是可以由存儲庫添加的對象的成員。 在此示例中,僅名字和姓氏。 其他數據(例如用戶上下文數據等)也可以在這些對象中傳遞,但是您不需要其他任何東西。

        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;
        }

其次是Get of Person實例。 您向其傳遞其后的人員實例的ID,然后將其檢索並返回PersonDto。 首先,您需要通過存儲庫從持久層獲取Person對象,然后需要將該對象轉換為Dto以返回給客戶端。 對於我使用AutoMapper的映射,這種類型的模式將極大地幫助您完成從一個對象到另一個對象的大量映射,這就是它的用途。

        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

如前所述,為此使用AutoMapper,查詢中的對象轉換之類的事情就變得輕松起來。

        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;
        }

出於完整性考慮,考慮到ListPersonDto僅包含名稱,上述自動映射器簽名看起來類似於以下內容。

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

因此,您的應用程序僅需要查看BAL和dto層。

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);
        }
    }
}

它增加了額外的工作,但是不幸的是,沒有簡單的方法可以完成,使用上面的模式將有助於分離事物,並使查找問題更加容易。 您的dto僅應具有操作所需的內容,因此可以更輕松地查看遺漏的地方或不包含的地方。 在這種情況下,AutoMapper是必不可少的,它使工作變得更加輕松,並減少了鍵入操作,周圍有很多使用automapper的很好的例子。

希望這有助於更接近解決方案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM