2009-02-05 2 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 ассоциированными сущностями
  • Создание новых объектов сайта с помощью нового связанные с ней организации
  • Принимая сайт (или сайты) и сохранение его и все связанные с ним объекты (если они изменились)

Итак, вернемся к моему вопросу, что должно быть этот объект ?

  • Существующий объект карты?
  • что-то на основе репозитория шаблона? *
  • что-то на основе единицы работы Паттен? *
  • Что-то еще?

* Я не полностью понимаю любой из них, как вы, вероятно, можете догадаться.

Есть ли стандартный способ решения этой проблемы, и может ли кто-нибудь дать краткое описание того, как они будут его реализовывать? Я не ищу, чтобы кто-то обеспечивал полностью работоспособную реализацию, только теорию.

Спасибо,
Джек

ответ

2

Использование шаблона хранилища/службы, ваши Repository классы будут предоставлять простой интерфейс 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, так почему дополнительный слой репозитория сверху? –

+0

Также, если у вас есть служба для каждого типа сущности или просто те, где вы хотели сделать что-то на заказ? –

+0

Я думаю, что репозиторий очень похож на ваших карточек, и вы, возможно, просто сохранили это. Новый компонент - это уровень обслуживания. – Misko

0

Я бы, вероятно, начать путем извлечения общей задачи на вспомогательный метод где-нибудь, а затем ждать, чтобы увидеть, что дизайн требует. Похоже, еще рано говорить.

Что бы вы назвали этим методом? Обычно это имя указывает, к чему относится метод.

+0

Хммм, очень хороший вопрос :-), я думаю, мне нужно дать это еще подумать. –

1

[Edit:. Эта запись делается попытка рассмотреть тот факт, что это часто проще писать собственный код непосредственно иметь дело с ситуацией, чем пытаться вписаться проблему в шаблон]

Patterns хороши в концепции, но они не всегда «на карте». После многих лет разработки 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(...)) 

Одна вещь, чтобы иметь в виду о программировании OO и PHP ... PHP не поддерживает «состояние» между запросами, поэтому создание экземпляра объекта только для его немедленного уничтожения нередко имеет смысл.

+0

На самом деле это не проблема проблемы с работой (создание новых, создание существующих и сохранение) нескольких связанных объектов. –

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

Хотя это изящное решение, которое не будет работать для моей настройки, согласно шаблону сопоставления данных, мои сущности не знают о картографе, поэтому у них нет метода сохранения. Объекты анализируются обратно к методу сохранения картографа для сохранения в БД. –

0

Сделайте свой сайт объектом Aggregate Root, чтобы инкапсулировать сложную ассоциацию и обеспечить согласованность.

Затем создайте SiteRepository, который несет ответственность за получение агрегата Сайта и заполнение его дочерних элементов (включая все страницы).

Вам не понадобится отдельный PageRepository (при условии, что вы не делаете страницу отдельным сводным корнем), и ваш SiteRepository должен также нести ответственность за объекты страницы (в вашем случае с помощью существующих 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, если вы хотите получить полную картину.

+0

ОК, поэтому вы настроили «хранилище» для выполнения аналогичной задачи на уровне обслуживания, предложенном CodeMonkey1? Смешение терминов меня действительно сбивает с толку (и это одна из причин, я должен задать этот вопрос), кажется, что (и не только на SO) люди используют Mapper, репозиторий и сервисный уровень взаимозаменяемо. –

+0

Вы правы, условия могут быть довольно запутанными - хотя это помечено как Domain Driven Design, поэтому я использовал условия DDD. В DDD у репозитория есть задача скрыть слой persistence до остальной части домена. Я не думаю, что в этом случае необходима услуга. –

Смежные вопросы