軟體的「貧血模型」(Anemic Domain Model)和「充血模型」(Rich Domain Model)是兩種在軟體設計中常見的模型設計風格,特別是在使用面向對象編程(Object-Oriented Programming,簡稱OOP)時,這兩種模型風格對於如何處理領域邏輯(Domain Logic)有著不同的理念。

DDD 本身就是著名的充血模型實作,而分散式拆分則是為貧血模型,以下將會細節說明。

貧血模型(Anemic Domain Model):

在貧血模型中,對象(Object)通常只是包含資料的純資料結構,並且缺乏有效的行為(方法)。這種模型將領域邏輯(Domain Logic)主要放在服務(Service)或管理類別中,而不是在對象本身。這意味著對象只是資料的容器,而所有處理邏輯都放在外部。 貧血模型的優點是簡單且易於理解,因為領域邏輯都放在一個中央位置,容易進行修改和維護。然而,它也有一些缺點。例如,當領域邏輯變得複雜時,服務類別可能會變得過度龐大,造成程式碼不易維護和測試。此外,貧血模型也未完全利用面向對象編程的優點,如封裝和多型性。

充血模型(Rich Domain Model):

相比之下,充血模型是一種較為嚴格的面向對象設計風格,將領域邏輯嵌入到對象中。這意味著對象不僅包含資料,還包含了處理資料的相關行為和邏輯。這使得對象能夠自主管理自己的狀態和行為,更符合真實世界中物件的行為。 充血模型的優點是更好地利用了面向對象編程的特性,更容易理解,也更符合物件導向的設計原則,如封裝和單一職責原則。此外,由於領域邏輯分散在不同的對象中,這使得程式碼更容易擴展和維護。 然而,充血模型可能在某些情況下增加了複雜性。當領域邏輯變得非常複雜時,對象之間的交互可能變得複雜,需要更深入的設計和理解。 在選擇使用貧血模型還是充血模型時,開發者需要考慮項目的需求、複雜性和可擴展性等因素,以及團隊成員對於這兩種設計風格的熟悉程度。

當談到「貧血模型」和「充血模型」,這兩種模型風格涉及領域邏輯的處理方式。

貧血模型(Anemic Domain Model)範例:

在貧血模型中,對象只是包含資料的純資料結構,而大部分的領域邏輯則放在服務(Service)類別中。

// User.php (Entity)
class User {
    private $id;
    private $name;
    private $email;
    // Getter and setter methods...
}

// UserService.php (Service)
class UserService {
    public function sendWelcomeEmail(User $user) {
        // Send a welcome email to the user...
    }
    public function generateUsername(User $user) {
        // Generate a unique username based on user's name and ID...
    }
}

// Example usage:
$user = new User(1, "John Doe", "[email protected]");
$userService = new UserService();
$userService->sendWelcomeEmail($user);
$username = $userService->generateUsername($user);

充血模型(Rich Domain Model)範例:

在充血模型中,對象不僅包含資料,還包含處理資料的相關行為和邏輯。

// User.php (Entity with Domain Logic)
class User {
    private $id;
    private $name;
    private $email;
    // Constructor...
    // Getter and setter methods...
    public function sendWelcomeEmail() {
        // Send a welcome email to the user...
    }
    public function generateUsername() {
        // Generate a unique username based on user's name and ID...
    }
}

// Example usage:
$user = new User(1, "John Doe", "[email protected]");
$user->sendWelcomeEmail();
$username = $user->generateUsername();

比較

在上面的兩個範例中,貧血模型的 User 類別只有資料,所有處理邏輯都放在 UserService 類別中。而充血模型的 User 類別則包含了處理邏輯,能夠自主管理自己的行為,這使得程式碼更具備物件導向的特性。 儘管充血模型可能會導致類別變得複雜,但它更符合物件導向設計原則,例如封裝和單一職責原則。貧血模型則可能比較簡單,但可能會導致服務類別變得過於龐大,難以維護和擴展。 在實際應用中,開發者需要根據項目需求和團隊成員的技術熟悉程度選擇合適的模型風格。

在 Laravel 框架中,確實有些設計上的特點可以被視為貧血模型導向。這是由於 Laravel 強調「瘦控制器、胖模型」的開發風格,即讓控制器盡可能簡單,將複雜的業務邏輯放在模型中。

好處

  1. 簡單易學:貧血模型導向的設計通常比較簡單,易於理解和學習。尤其對於初學者或新手開發人員來說,這種風格可能更容易上手。
  2. 專注業務邏輯:在 Laravel 的貧血模型中,模型負責處理業務邏輯,包括驗證、資料庫操作等。這使得控制器可以更專注於接收請求和發送回應,讓程式碼更具可讀性。
  3. 易於測試:貧血模型將業務邏輯放在模型中,使得業務邏輯可以更輕鬆地進行單元測試,而不依賴於外部資源。

壞處

  1. 違反物件導向設計原則:貧血模型有時可能違反物件導向設計原則中的「充血模型」原則,即將行為和資料封裝在一起。這可能導致模型變得複雜且難以維護。
  2. 過於膨脹的模型:隨著業務邏輯的增加,模型可能變得過於膨脹,導致單一模型的職責過多,不利於程式碼的管理和維護。
  3. 缺乏可重用性:貧血模型可能缺乏可重用性,因為業務邏輯被耦合到特定的模型中,很難在其他地方重用。

在實際開發中,選擇使用貧血模型還是充血模型取決於項目的需求和團隊的開發風格。在小型應用中,貧血模型可能是一個簡單有效的選擇,但在大型複雜應用中,充血模型可能更合適,可以更好地管理和組織領域邏輯。無論採用哪種風格,保持程式碼的整潔、可讀性和可維護性都是非常重要的。

ref: