工作有些年頭了一直在搬磚,下定決心從零開始寫一套領域模型的項目

把知道的東西變成會的,把會的東西融會貫通

最終能不能用無所謂,總要留點什麼東西

 

Github 倉庫地址

每一篇文章對應一個 tag

這版代碼定義了

  領域模型中需要那些層

  層與層之間的基本引用

  使用.NET Core 內置的依賴注入框架,對各層之間進行解耦 (後面會替換成 Autofac)

 

以下內容都只是我本人的理解,可能對也可能不對

 

由於項目之間只引用了接口沒有引用實現,所以直接編譯後的DLL只有接口沒有實現

因此:

  一、各層編譯後的DLL,都輸出到同一個目錄下

  二、Web項目,編譯的時候把該項目中所有的DLL複製到 bin 文件中對應的路徑下

 

項目結構

0-Infrastructure

  Core2022.Enum

  Core2022.Framework.Commons    定義一些公共方法

  Core2022.Framework    定義Domaim、Repository、UnitOfWork 基本實現,IoC、Middleware等實現

1-Presentation  

  Core2022.API 

  Core2022.Web 

2-Application

  Core2022.Application.Services

  Core2022.Application.Services.DTO    表現層與應用層之間交互用的對象

  Core2022.Application.Services.Interface 

3-Domain

  Core2022.Domain

  Core2022.Domain.Model    ORM對象

  Core2022.Domain.Interface

4-Repository

  Core2022.Repository

  Core2022.Repository.Interface

 

概念:

表現層:負責向用户顯示或者收集用户輸入的指令。(Web、API、WCF 等)

應用層:對應一個具體的業務,指揮領域對象來解決問題。

領域層:負責具體的業務概念、狀態、規則等信息。

基礎設施層:為各層提供支持,為表現層提供中間件等、為應用層提供信息的傳遞、為領域層提供數據的持久化

倉儲層:某個領域獨有的方法(獲取修改數據的方法),放到該領域的倉儲層中。

 

 

領域對象與ORM對象

ORM對象,只是一個用來和數據庫表字段映射用的類型,他只能包含一些簡單的屬性(姓名、年齡等)

領域對象,ORM對象只是領域對象的一個屬性(私有屬性),領域對象通過 Get,Set方法訪問或者修改ORM對象。

        GetName() { return ORM對象.Name }

        SetName(string name) { ORM對象.Name = name }

        領域對象還包含其他屬性(所在部門、直屬領導)。

        UserDomain.GetDept() { return DeptDomain } 獲取員工所在的部門

        UserDomain.GetLeader() { return UserDomain } 獲取員工的直屬領導

        UserDomain.GetCustome(Guid userKeyId)  { return List<CustomerDoamin> } 獲取該員工的客户列表

     領域對象還包含了對象的行為、規則(發送消息)。

        UserDomain.SendInformation(string msg) { ... } 發送消息行為

        UserDomain.AddCustome(xxx) { 員工增加100塊收入,部門任務整體獎金增加 } 增加客户規則

 

在快速開發的時候,往往為了效率經常是怎麼簡單怎麼來。因此業務邏輯會分散到表現層,應用層,數據層,數據庫腳本,導致的後果就是後續分析問題的時候異常的困難。

 

領域模型的高內聚、合理的分層可以解決這些問題。

但是領域模型也有他自己的問題

  --select * from

  UPDATE t1 set t1.UserName = 'XXXX' from
 
 [user] as t1
  inner join Dept as t2 on t1.KeyId = t2.UserKeyId
  where t1.KeyId = 'F4095679-83B6-4646-9BFA-7745D7CD3AD0'

  比如我要修改某個員工他所在部門的名字,一條SQL就能完成,但是在領域模型中需要先來個 UserDomain 在通過 UserDomain 獲取到 DeptDomain 在調用 DeptDomain.SetDeptName() 修改這個部門名字

  

  再比如 UserDomain.GetCustome() 獲取到一個 List<CustomerDoamin> 不管業務是什麼,只要用到客户列表就會執行 Select [ORM對象中的所有字段] from Customer where UserKeyId = 'XXXXXX'。

 

 

引用關係

0-Infrastructure
	  Core2022.Enum
	  Core2022.Framework.Commons
	  Core2022.Framework
1-Presentation
  Core2022.API
  Core2022.Web
    Core2022.Application.Services.DTO
    Core2022.Application.Services.Interface
2-Application
  Core2022.Application.Services
    Core2022.Application.Services.DTO
    Core2022.Application.Services.Interface
    Core2022.Domain.Model
    Core2022.Domain.Interface
    Core2022.Repository.Interface
  Core2022.Application.Services.DTO
  Core2022.Application.Services.Interface
    Core2022.Application.Services.DTO
3-Domain
  Core2022.Domain
    Core2022.Domain.Model
    Core2022.Domain.Interface
  Core2022.Domain.Model
  Core2022.Domain.Interface
4-Repository
  Core2022.Repository
    Core2022.Domain.Model
    Core2022.Domain.Interface
    Core2022.Repository.Interface
  Core2022.Repository.Interface
    Core2022.Domain.Model
    Core2022.Domain.Interface

 

依賴注入

在 Startup 中配置依賴注入,實現各層之間的解耦

InjectionServicesExtension.cs

using Core2022.Framework.Attributes;
using Core2022.Framework.Settings;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Reflection;

namespace Core2022.Framework.Commons.Injections
{
    /// <summary>
    /// 
    /// </summary>
    public static class InjectionServicesExtension
    {
        /// <summary>
        /// 通過反射註冊項目中的所有服務
        /// </summary>
        /// <param name="services"></param>
        /// <param name="configuration"></param>
        /// <returns></returns>
        public static void InjectionServices(this IServiceCollection services, IConfiguration configuration)
        {
            foreach (var assemblyString in AppSettings.InjectionServices.AssemblyStrings)
            {
                var serviceTypes = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + assemblyString).GetTypes();
                if (serviceTypes != null && serviceTypes.Length > 0)
                {
                    foreach (var service in serviceTypes)
                    {
                        var attribute = service.GetCustomAttribute(typeof(InjectionAttribute), false);
                        if (attribute is InjectionAttribute)
                        {
                            InjectionAttribute injectionAttribute = attribute as InjectionAttribute;
                            var serviceInterfaceName = injectionAttribute.ServiceInterfaceName;
                            var serviceLifetime = injectionAttribute.ServiceLifetime;
                            AddInjectionWithLifetime(services, serviceLifetime, serviceInterfaceName, service);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 向依賴框架中註冊服務
        /// </summary>
        /// <param name="services">依賴框架</param>
        /// <param name="serviceLifetime">服務生命週期</param>
        /// <param name="service">服務</param>
        /// <param name="implementation">服務的實現</param>
        /// <returns></returns>
        private static IServiceCollection AddInjectionWithLifetime(IServiceCollection services, ServiceLifetime serviceLifetime, Type service, Type implementation)
        {
            switch (serviceLifetime)
            {
                case ServiceLifetime.Scoped:
                    return services.AddScoped(service, implementation);
                case ServiceLifetime.Singleton:
                    return services.AddSingleton(service, implementation);
                case ServiceLifetime.Transient:
                    return services.AddTransient(service, implementation);
                default:
                    return services;
            }
        }
    }
}

 

InjectionAttribute.cs

using Microsoft.Extensions.DependencyInjection;
using System;


namespace Core2022.Framework.Attributes
{
    public class InjectionAttribute : Attribute
    {
        /// <summary>
        /// 服務依賴的接口
        /// </summary>
        public Type ServiceInterfaceName { get; set; }

        /// <summary>
        /// 註冊服務的生命週期
        /// </summary>
        public ServiceLifetime ServiceLifetime { get; set; }

        /// <summary>
        /// 依賴特性
        /// </summary>
        /// <param name="name">服務依賴接口</param>
        /// <param name="serviceLifetime">服務生命週期</param>
        public InjectionAttribute(Type serviceInterfaceName, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)
        {
            ServiceInterfaceName = serviceInterfaceName;
            ServiceLifetime = serviceLifetime;
        }
    }
}

 

Startup.ConfigureServices

public void ConfigureServices(IServiceCollection services)
{
    services.InjectionInjectionOrmModel();
    //註冊控制器和視圖
    services.AddControllersWithViews();
    //註冊服務
    services.InjectionServices(Configuration);
}

 

appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",

  /*註冊服務 配置*/
  "InjectionServices": {
    "AssemblyStrings": [
      "Core2022.Domain.Model.dll",
      "Core2022.Application.Services.dll",
      "Core2022.Domain.dll",
      "Core2022.Repository.dll"
    ]
  }
}

這一版代碼都屬於基礎知識,都是固定用法

唯一麻煩的就是編譯項目的時候需要編譯整個解決方案