2012-06-28 71 views
1

我正在設計Web應用程序的基礎體系結構。該項目遵循Domain-Driven Design方法,因爲業務模型和邏輯非常複雜。服務層和模型與域驅動設計的關聯

該項目還旨在成爲一個SOA項目(面向服務的體系結構)。所以我正在學習很多關於服務以及如何圍繞它來構建項目。

previous question of mine之後,我有一個關於模型類中的關聯的問題。

我明白,模型類不應該知道和做任何有關持久性。然而,我很難決定模型類之間的關聯。

例如:

  • Person
  • Car有(爲例子)

一個驅動程序getDrivergetCars應放在何處?

  1. 在模型類:$car->getDriver()
  2. 與原始類型服務層
  3. :在服務層$personService->getPerson($car->getDriverId())
  4. 使用OOP:$carService->getDriver($car)

溶液1似乎更自然。我正在使用Doctrine 2,因此模型關聯會使用數據庫映射註釋處理。這樣,模型不會做任何與持久性相關的事情(儘管實際上通過Doctrine來做)。這是我最喜歡的解決方案,但除了加載「汽車」列表之外,本服務的重點是什麼?因爲它扔掉OOP和型號/服務的用戶必須知道有關數據庫模型獲取協會(他已經知道這ID是一個「人」的ID)

解決方案2.似乎只是愚蠢。他必須親自去做這個協會。

解決方案3比解決方案2好一點,但仍然是其中OOP在那?

所以,對我來說解決方案1.是最好的。但我已經看到了解決方案2和解決方案3在實際項目中使用(有時混合在一起),所以我有疑問。

而且,問題就變成當有額外的參數比較複雜,例如:

$person->getCars($nameFilter, $maxNumberOfResults, $offset); 

(在這種情況下,它確實看起來像一個SQL查詢/持久查詢)

那麼,哪一個應該用於模型/服務體系結構上的項目跟在域驅動設計的方法?有了SOA,我的模型應該只是沒有邏輯的「啞」數據容器嗎?如果是這樣,那麼DDD方法在哪裏?

回答

1

在DDD的背景下,這是一個決定實體之間的關係是否通過直接對象關聯與存儲庫表達的問題。兩種方法都是有效的,並取決於關係的性質。例如,在您的域名中,一個人可能會擁有很多與他們相關的汽車,並且直接關聯到來自該個人實體的一組汽車並沒有意義。請記住,實體的工作,或者更確切地說是聚合根,是爲了保護不變式和執行業務規則。如果與人員相關聯的一組車輛不需要存在於人員類別上的任何行爲,則沒有理由將該人員關聯放置在人員實體上。此外,正如您的示例所示,可能需要對汽車查詢進行過濾。爲了回答你的問題,我將把代表人與車關係的責任放在repository。 SOA與DDD正交,更側重於如何訪問和部署業務功能。從hexagonal architecture的角度來考慮DDD和SOA之間的相互作用也是有益的,這也被稱爲onion architecture。您的域位於核心,由一組應用程序服務封裝,這些應用程序服務在您的域周圍形成一個API外觀。這些與SOA中的服務不同,後者是六邊形/洋蔥體系結構中的端口/適配器,它們將這些應用服務公開爲SOA服務。

+0

確定首先我明白我混合了SOA和應用程序服務。實際上,所有關於關聯的問題都依賴於一個實體是否是一個聚合根。如果是,我應該使用存儲庫。如果它是一個聚合的子實體,那麼我應該使用對象關聯。我對麼? –

1

如果您的項目是DDD,我不明白爲什麼您需要模型/服務體系結構。國際海事組織這創造了貧血模型,一切都非常程序化。

現在,作爲DDD它意味着你不關心數據庫。儘管(至少在邏輯上)2模型:域和持久性。領域模型以最自然的方式處理關聯,最適合代表商業案例。 '有一個司機',或者有很多是以db爲中心的想法,在DDD中沒有地位。持久性模型處理聚合根將存儲在數據庫中的方式(這裏是您定義ORM實體及其關係等)。

關於您的問題,首先它關係的上下文和目的。如果它嚴格用於查詢(向用戶顯示),則可以使用簡單模型,不需要DDD和業務規則。控制器可以直接詢問專用查詢存儲庫中的數據,作爲DTO返回。

如果你想更新個人或汽車,然後在應用層(我通常使用基於命令的方法,所以這些都發生在命令處理程序中,但在架構上它仍然是應用程序的一部分圖層),您可以從(域)存儲庫中撤回最適合該任務的AR。域存儲庫知道getPerson($ id)應該返回一個域實體,而不是返回一個簡單的DTO的查詢庫。

$person=$repo->getPerson($id); 
//do stuff 
$repo->save($person); 
//optionally raise event (if you're using the domain events apprach) 

但是棘手的是決定在什麼情況下AR是什麼。一輛車在什麼情況下有一個司機?司機真的屬於一輛車?有沒有業主的概念?你有Person類,但是一個人可以是司機或者所有者(或者如果它是租賃公司的話)。正如你看到的,它很大程度上取決於域,只有當你有了一個清晰的域名映像後,你纔可以開始考慮如何存儲數據以及存儲庫返回哪個對象(實體)。

+0

你提出了很好的觀點。首先,我絕對想避免一個「貧血模型」,這實際上是我質疑的根源。這是模型/服務體系結構必須是什麼?我很困惑:我應該丟掉服務層來保持模型中的邏輯嗎? (是的,領域模型非常複雜並且證明了DDD的合理性,我給出的例子只是一個非常基本的例子)。 –

+1

保留域中的相關邏輯,它屬於那裏。將域服務用於屬於域但不自然適合域對象的操作。用於基礎設施問題的其他層的服務(例如授權)。 – MikeSW

0

當考慮到什麼地方時,考慮服務和模型的目的。服務駐留在application layer而模型駐留在域圖層中。那麼,你的應用程序需要知道什麼Person?可能不是很多。用戶界面可能會發送一些ID來處理請求的操作。

這裏,AR是Driver模型。請記住,服務可能包含其他服務,並且該原則的要求是POPOs,並且不需要是anemic。另外,嘗試將開發思維過程從持久化中分離出來。例如,$driverId不需要是整數,它可以是任何與域相關的唯一標識符。

// DriverService 
// If more parameters are needed, consider passing in a command object 
public function beginTrip($driverId, $carId, $fromLocationId, $toLocationId) 
{ 
    $driver  = $this->repository->find($driverId); 
    $car   = $this->carService->getAvailableCar($carId, $driverId); 
    $withItenerary = $this->locationService->buildItenerary(
     [$fromLocationId, $toLocationId] 
    ); 

    $driver->drive($car, $withItenerary); // actual 'driving' logic goes here 
    $this->eventService->publish(new BeginTripEvent($driver, $car, $withItenerary)); 
} 
+0

注意:我知道這是一個necro-thread,但它還沒有被回答。所以,我發佈我的回覆,以防其他人幫忙。 – texdc

0

確定第一我明白我是混合SOA和應用服務。

是的。您正在使用域對象和服務層(SOA)方法將域層(DDD)方法與數據傳輸對象混合在問題中。

域層對象是與服務層對象不同的對象!例如,服務層可能有一個對象CarDTO而不是Car對象和DriverDTO對象而不是Driver對象。

  1. $car->getDriver()是訪問領域層Driver完全正確的方法,並且還可以在服務層有限制,即無論消費者服務請求Car數據,服務總是返回一個Driver一個Car使用。

  2. $personService->getPerson($car->getDriverId())僅在服務層有效,在域層無效。此方法的原因是Driver數據太大且複雜,因此總是會返回Car。因此,服務提供了一種單獨的方法來請求Driver數據。

  3. $carService->getDriver($car)是在領域層和無效,在陌生的服務層看到的,因爲這結構意味着服務消費必須全部Car數據發送到一個CarService得到Driver數據。最好只發送CarID,也許發送到PersonService,而不發送到CarService(變體2)。

更復雜的例子$person->getCars($nameFilter, $maxNumberOfResults, $offset);看起來領域層奇怪的,因爲它不包含的業務邏輯。但是,如果更改爲$CarService->getCars($nameFilter, $maxNumberOfResults, $offset);,它將適用於部分請求的服務層。