2012-06-28 42 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);,它将适用于部分请求的服务层。