以領域為中心的:線上房貸申請系統設計 use DDD

以領域為中心的:線上房貸申請系統設計 use DDD

線上房貸申請系統的領域模型

前言

DDD 的出現,為的是解決軟體專案開發過程中,不同的專業領域 或 經驗背景、技術專家 與 領域專家 溝通時,你講的跟我講的可能不一樣!?或你講的,跟理解的,跟我想的可能不一樣?... 是的!軟體專案開發最擔心的就是如此,專案會失敗大部分的原因也可能是如此,也因此 2003 年左右,Eric Evans 提出了 DDD, Domain-Driven Design 的思想與概念,

需求內容

圖(一)、案例一:線上房貸申請系統 案例一:線上房貸申請系統

需求以一個〔線上房代申請系統〕為例,關於這個需求讀者可以參考筆者先前寫過的幾篇 DDD 與 c4Model 的相關文章,是以線上房貸申請系統為例的,而在更早之前我撰寫過一個決戰 OOAD 的線上課程,課程中我們只是以基礎的 OOAD 並搭配 MDA 為出發點進行設計,現在,我想改以 DDD 領域驅動設計的方式重新設計這個線上房貸申請系統,上過此課程的學員也可以重新再練習一次,並跟著同時學習 DDD 的設計方式,因為同樣以 MDA 角度為出發點來設計。

領域敘事 (Domain Storytelling)

這邊我習慣以領域敘事來描述領域專家的的需求任務,原因除了團隊與個人習慣外,無其他特別因素。

圖(二)、顧客線上申請房貸的領域敘事 顧客線上申請房貸的領域敘事

DDD 與軟體架構設計

DDD 其實不管軟體架構設計的,原因稍後會提到,DDD 大致分為兩大部分,也就是『戰略(Strategic)』、『戰術(Tactical)』,戰略的部分主要在解決領域需求上,不同角色間的協作與溝通,盡量能說出共通語言,或者建立共通的語言,也是 DDD 一直強調的 Ubiquitous Language 通用語言,並以此為基礎來建立領域知識,因為不同領域(銀行/流通業/金融業...)總是有個領域的專業術語,而在分析解決商業需求時,使用大家都能懂沒有隔閡的語言來溝通相當的重要。

圖(三)、解決領域需求與建模 解決領域需求與建模

線上房貸申請系統的領域模型

在本篇文章中,我加入〔額度利率試算〕這個 Context,原因是它經過需求提煉的過程中發現是可以獨立開來的 Context, 因此直接拆另一個 Bounded Context,而根據需求,需求中銀行顧客可以到線上房貸申請首頁,使用網銀帳號或初次申請進行簡訊驗證後方可填寫〔基本資料〕與〔貸款資料〕,因此我將客戶與網銀帳好的管理切割為兩個 Bounded Context,並且也順應需求將〔額度利率試算 (Interest rate)〕也加入需求中,但我目前只是將它切割開,暫時不設計它 XD

圖(四)、線上房貸申請-反覆設計的 Domain Modeling 線上房貸申請-反覆設計的 Domain Modeling

使用 Clean Architecture

其實我一直想要澄清一點就是,整潔架構 Clean Architecture 與 DDD 其實一點關係都沒有,那為什麼最近幾年人們探討 DDD 的時候,總是將整潔架構牽扯在一起?或甚至認為談 DDD 就應該談整潔架構?事實上 DDD 這個概念提出時當時市場上只有六角架構 Hexagonal Architecture、或者 2008 年的洋蔥式架構 Onion Architecture 等,但因為整潔架構所提供的彈性非常高,所以後來人們都使用整潔架構來作為 DDD 的主要設計方針,甚至連 Microservices 也被與 DDD 牽扯在一塊,好像使用 Microservices 就一定要使用 DDD,但這兩者其實一點關係都沒有,早期單體式架構設計也可以使用整潔架構 Clean Architecture 啊!早期 DDD 思想剛出來時,整潔架構的概念根本就還沒出來 XD,但是初學者往往搞不清楚。

另外,熟讀過洋蔥架構與六角架構的讀者應該會發現,他們與後來的整潔架構的核心思想概念與出發點幾乎是相同的。

Ok, 說了那麼多,現在來說說,我會怎麼樣來設計這個房貸的線上申請系統?而且是基於整潔架構,先說明我會以 .NET 5 的 MVC Website 來設計這個系統,並且先不套用 CQRS,大部分原因是我並不想使用過多的第三方套件像是大家都善於使用的 MediatR 的套件,我盡量以原生的 .NET 5 來完成這個專案。

這邊的程式碼與領域模型的對應,我現在習慣先用 LINQPad 以最少的時間寫出來,一來與 Domain Modeling 能快速的對應,也讓自己容易與 Domain Expert 溝通細節。

初步快速設計的程式碼如下(花費 10 多分鐘而已):

#region Domain
public interface IAggegateRoot
{
}

public class Entity
{
}

public class Customer: Entity, IAggegateRoot
{
    private string accountId;

    public string GetAccountId()
    {
        return accountId;
    }

    public void SetAccountId(string value)
    {
        accountId = value;
    }

    private string chtName;

    public string GetChtName()
    {
        return chtName;
    }

    public void SetChtName(string value)
    {
        chtName = value;
    }
}

public class HousingLoansData: Entity, IAggegateRoot
{
}

public interface ICustomerDetailRepository
{
    int AddBasicCustomerDetailData(Customer customer);
}
#endregion

#region Application
public class CustomerDetailDTO
{
    public string UserId { get; set; }
    public string ChtName { get; set; }
    public string Location {get;set;}
}

public class GetCustomerDetailQuery
{
    private ICustomerDetailRepository _customerDetailRepository;

    public GetCustomerDetailQuery(ICustomerDetailRepository customerDetailRepository)
    {
        _customerDetailRepository = customerDetailRepository;
    }

    public IEnumerable<CustomerDetailDTO> Get()
    {
        return null;
    }
}

public class CustomerBasicDetailRegisterHandler
{
    private ICustomerDetailRepository _curstomerDetailRepository;
    
    public int AddCustomerDetailData(CustomerDetailDTO customerDeatil)
    {
        Customer customer = new Customer() {};
        // Process and others..
        return _curstomerDetailRepository.AddBasicCustomerDetailData(customer);
    }
}
#endregion

#region Infrstructure
public class CustomerDetailRepository : ICustomerDetailRepository
{
    public int AddBasicCustomerDetailData(Customer customer)
    {
        throw new NotImplementedException();
    }
}
#endregion

上方以 C# 進行設計,並以 C# 的 Region 區塊先加以區分了 Application 與 Domain 和 Infrstructure 層次出來了,下一步就是建立實際的 .NET 5 Web App 專案將程式碼放置鄧正確的位置即可。

以領域為核心重新設計的線上房貸申請系統

待續。。。XDDD

*** 本篇文章會持續的更新 ***

留言

這個網誌中的熱門文章

軟體工程師 - 成長的 10 個階段

常見的程式碼壞味道(Code Smell or Bad Smell)

什麼是 gRPC ?