2009-02-05 73 views
2

更新: 09/02/2009 - 修订后的问题,提供了更好的示例,增加了赏金。封装通用逻辑(域驱动设计,最佳实践)


嗨,
我建设使用的数据库和实体(域对象)之间的数据映射器模式的PHP应用程序。我的问题是:

封装通常执行的任务的最佳方法是什么?

例如,一个常见任务是从站点映射器中检索一个或多个站点实体,以及从页面映射器中检索它们的关联(主页)实体。目前,我是这样做的:

$siteMapper = new Site_Mapper(); 
$site = $siteMapper->findByid(1); 

$pageMapper = new Page_Mapper(); 
$site->addPage($pageMapper->findHome($site->getId())); 

现在,这是一个相当简单的例子,但它能够在现实中更复杂,因为每个网站也有一个相关联的场所,并在页面实际上有多个版本(虽然为了这个任务的目的,我只对最近的一个感兴趣)。

我需要在我的应用程序中的多个位置执行此操作(获取站点和相关主页,区域设置等),并且我不能想到封装此任务的最佳方式/位置,以至于我不必在各处重复。理想情况下,我想有这样的事情结束了:

$someObject = new SomeClass(); 
$site = $someObject->someMethod(1); // or 
$sites = $someObject->someOtherMethod(); 

其中所得的网站实体已经有了相关的实体创建,并准备使用。

保存这些对象时发生同样的问题。说我有一个网站的实体和相关主页实体,他们已经都被修改,我必须做这样的事情:

$siteMapper->save($site); 
$pageMapper->save($site->getHomePage()); 

同样的,琐碎的,但这个例子被简化。代码的重复仍然适用。

在我的脑海里是有意义的有某种中央对象的,可以照顾:

  • 检索网站(或网站)和所有nessessary相关实体
  • 创建新的新网站的实体相关实体
  • 以一个网站(或网站)并保存它和所有相关实体(如果他们已经改变)

所以,回到我的问题,我应该为此对象?

  • 现有的mapper对象?
  • 东西基于存储库模式?*
  • 基于东西彭定康工作的单位吗?*
  • 别的东西吗?

*我不完全理解其中任意一个,因为你可能已经猜到了。

有没有一种标准的方法来解决这个问题,有人可以提供他们如何实现它的简短描述?我不是在寻找任何人来提供一个完整的工作实现,只是理论。

感谢,
杰克

回答

2

使用储存库/服务模式,你的存储库的类将提供为每个实体的一个简单CRUD接口,那么服务类将是执行像安装实体的依赖关系的附加逻辑的附加层。然后,您的其他应用只能使用服务。你举的例子可能是这样的:

$site = $siteService->getSiteById(1); // or 
$sites = $siteService->getAllSites(); 

然后SiteService类中你会有这样的事情:

function getSiteById($id) { 
    $site = $siteRepository->getSiteById($id); 
    foreach ($pageRepository->getPagesBySiteId($site->id) as $page) 
    { 
    $site->pages[] = $page; 
    } 
    return $site; 
} 

我不知道PHP是很好,所以请原谅,如果有语法错误的东西。

+0

好的,这是有道理的,需要阅读服务模式。但是,(我已经遇到了这个问题),存储库和映射器之间有什么区别。我的映射器已经提供了一个CRUD界面,为什么附加的仓库层位于顶层? – 2009-02-09 20:07:26

+0

另外,您是否应该为每种类型的实体提供服务,或者只是您想要做某些事情的服务? – 2009-02-09 20:08:48

0

我可能会通过提取共同任务某处一个辅助方法开始,然后等着看的设计要求是什么。感觉现在讲得还为时过早。

你会说这个方法是什么?该名称通常暗示该方法所属的位置。

+0

嗯,非常好的问题:-),我想我需要给这个更多的想法。 – 2009-02-06 09:59:17

1

[编辑:该行试图解决的事实,这是经常容易编写自定义代码来直接与情况处理比它要尽量合身的问题转化为模式]

模式在概念上很好,但它们并不总是“映射”。经过多年高端PHP开发,我们已经以一种非常直接的方式处理这些问题。试想一下:

文件:Site.php

class Site 
{ 
    public static function Select($ID) 
    { 
     //Ensure current user has access to ID 
     //Lookup and return data 
    } 

    public static function Insert($aData) 
    { 
     //Validate $aData 
     //In the event of errors, raise a ValidationError($ErrorList) 

     //Do whatever it is you are doing 

     //Return new ID 
    } 

    public static function Update($ID, $aData) 
    { 
     //Validate $aData 
     //In the event of errors, raise a ValidationError($ErrorList) 

     //Update necessary fields 
    } 

然后,为了把它(从任何地方),只需运行:

$aData = Site::Select(123); 

Site::Update(123, array('FirstName' => 'New First Name')); 

$ID = Site::Insert(array(...)) 

有一件事要记住关于面向对象编程和PHP ... PHP不会在请求之间保持“状态”,所以创建一个对象实例只是为了让它立即销毁通常没有意义。

+0

这并没有真正解决处理多个相关实体(创建新的,创建现有的和保存)的问题。 – 2009-02-09 19:02:01

0
class Page { 

    public $id, $title, $url; 

    public function __construct($id=false) { 
    $this->id = $id; 
    } 

    public function save() { 
    // ... 
    } 

} 

class Site { 

    public $id = ''; 
    public $pages = array(); 

    function __construct($id) { 
    $this->id = $id; 
    foreach ($this->getPages() as $page_id) { 
     $this->pages[] = new Page($page_id); 
    } 
    } 

    private function getPages() { 
    // ... 
    } 

    public function addPage($url) { 
    $page = ($this->pages[] = new Page()); 
    $page->url = $url; 
    return $page; 
    } 

    public function save() { 
    foreach ($this->pages as $page) { 
     $page->save(); 
    } 
    // .. 
    } 

} 

$site = new Site($id); 
$page = $site->addPage('/'); 
$page->title = 'Home'; 
$site->save(); 
+1

虽然这是一个优雅的解决方案,但根据数据映射器模式,这不适用于我的设置,因为我的实体不知道映射器,所以它们没有保存方法。将实体解析回映射器的保存方法以保存到数据库。 – 2009-02-09 19:00:09

0

使您的Site对象为Aggregate Root来封装复杂关联并确保一致性。

然后创建一个SiteRepository,负责检索网站聚合并填充其子(包括所有网页)。

您不需要单独的PageRepository(假设您不会将页面设置为单独的聚合根目录),并且您的SiteRepository应该有责任检索Page对象(在您的情况下,通过使用现有的Mappers) 。

所以:

$siteRepository = new SiteRepository($myDbConfig); 
$site = $siteRepository->findById(1); // will have Page children attached 

然后是findById方法将负责也发现本网站的所有页面的孩子。这与CodeMonkey1给出的答案有相似的结构,但是我相信通过使用Aggregate和Repository模式,您将获得更多的收益,而不是为此任务创建特定的服务。网站聚合的任何其他检索/查询/更新,包括其任何子对象,都将通过相同的SiteRepository完成。

编辑:Here's a short DDD Guide来帮助你的术语,但我真的建议阅读Evans如果你想要整个图片。