Как обрабатывать связь между доменом и уровнями базы данных? - PullRequest
0 голосов
/ 14 февраля 2009

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

Уточнение Мое существующее решение использует Data Mappers для непосредственного взаимодействия с базами данных. Однако, как я уже исследовал эту проблему, многие люди предложили, чтобы уровень домена не связывался напрямую и не содержал картографы данных, которые фактически выполняют взаимодействие с базой данных. Вот почему я поместил объекты Repository между Доменом и необходимыми Data Mappers, но это не кажется вполне естественным или правильным. Таким образом, реальный вопрос заключается в том, какой уровень естественным образом существует для обработки связи между Доменом и Устройствами отображения данных? Любые примеры того, как структурировать это, будут оценены.

Например:

  • Как правильно обрабатывать получение коллекции объектов домена в контексте другого объекта домена?
  • Как принудительно вставить один объект домена или коллекцию объектов на основе действия, выполненного над другим объектом. В настоящий момент я сталкиваюсь с тем, что, когда Лицо присоединяется к Кампании, мне нужно вставить все События, которые необходимо выполнить для этого Лица для этой Кампании.

Ответы [ 4 ]

4 голосов
/ 31 июля 2009

Существует различие между моделью предметной области и ее реализацией. То, что ваша модель показывает отношение Person ---> Campaign ---> Event, не означает, что вы должны реализовать его таким образом. Итак, ваша модель показывает ваш анализ и дизайн объектно-ориентированным способом, но вы реализуете эту модель в ООП, которая ограничена тем, насколько хорошо она может копировать эту модель в коде.

Рассмотрим следующее.

A Person не определяется его владением Campaign, поэтому кампанию можно исключить из сферы своих знаний. С другой стороны, Campaign определяется Event s, которые происходят как часть его выполнения, поэтому будет справедливо иметь коллекцию событий в рамках кампании. Я хочу сказать, что у каждого класса должно быть достаточно поведения и знаний, чтобы сделать его единым целым.

Что касается связи между доменом и постоянными уровнями, рассмотрим их как две совершенно разные системы, которые не связаны друг с другом. Все они знают, каковы его обязанности и какие объявления он делает. Например, уровень постоянства знает, как сохранить данные, переданные ему, и объявить, что данные были сохранены. Однако постоянный уровень не обязательно должен понимать доменные объекты. Аналогично, доменный уровень понимает Person, Campaign и Event, но ничего не знает о постоянстве.

Следствием вышесказанного является то, что уровень домена должен быть сам по себе и не должен зависеть от уровня постоянства для своих данных. Тем не менее, он все еще должен быть снабжен данными для выполнения своих обязанностей. Эти данные могут поступать либо из пользовательского интерфейса, либо из базы данных, и передаются ему через стороннее устройство, которое знает как об уровне домена, так и об уровне персистентности.

Итак, в коде (псевдо-C #) ...

namespace DomainLayer
{
    interface IDomainListener 
    {
        void PersonCreated(Person person);
    }

    class Person
    {
        private string name;

        public Person(string name)
        {
            this.name = name;
        }

        public string Name
        {
            get { return name; }
        }
    }

    class Domain 
    {
        private IDomainListener listener;

        public Domain(IDomainListener listener) {
            this.listener = listener;
        }

        public void CreatePerson(string name) {
            Person person = new Person(name);
            listener.PersonCreated(person);
        }
    }
}


namespace PersistenceLayer
{
    interface IPersistenceListener
    {
        void PersonDataSaved(int id, object data);
    }

    class Persistence
    {
        private IPersistenceListener listener;

        public Persistence(IPersistenceListener listener) 
        {
            this.listener = listener;
        }

        public void SaveData(object data)
        {
            int id = ...; // save data and return identifier
            listener.DataSaved(id, data);
        }
    }
}

namespace MyApplication
{
    class MyController : IDomainListener, IPersistenceListener
    {
        public void CreatePersonButton_Clicked()
        {
            Domain domain = new Domain(this);
            domain.CreatePerson(NameTextbox.Text);
        }

        public void PersonCreated(Person person)
        {
            Persistence persistence = new Persistence(this);
            persistence.SavePersonData(person.Name);
        }

        public void DataSaved(int id, object data)
        {
            // display data on UI
        }
    }   
}

Как видите, пространства имен представляют разные уровни. Интерфейсы XYZListener определяют объявления, сделанные на уровне XYZ. Любые другие уровни, которые заинтересованы в этих объявлениях и будут отвечать на них, должны реализовывать эти интерфейсы, как и наш MyApplication уровень.

При нажатии кнопки «Создать» контроллер создает фасадный объект Domain для слоя домена и регистрирует себя в качестве прослушивателя. Затем он вызывает метод CreatePerson, который создает экземпляр Person, а затем объявляет, что это было сделано, передавая новый экземпляр. Контроллер отвечает на это объявление в реализации PersonCreated, где он порождает фасад уровня персистентности и снова регистрируется как слушатель. Затем он вызывает метод SaveData, который объявляет DataSaved по завершении. Реализация этого метода затем отображает данные в пользовательском интерфейсе.

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

Возвращаясь к вашей конкретной проблеме, у вас может быть метод FindPerson для персистентности, который объявляет PersonFound(int id). Ответ контроллера будет вызывать постоянный уровень для получения данных о кампании и событиях, а затем вызывать уровень домена с этими данными для построения Person.

Извините за длинный ответ ...

2 голосов
/ 14 февраля 2009

Габриэль, это называется « проблема согласования импедансов ». Существует множество решений, от таких тяжеловесных, как бины сущностей J2EE, до Ruby ActiveRecord и простого кодирования ручного соединения.

Обновление

Ладно, трудно понять, как именно на это напасть, без лишней информации, но вот основной подход.

Любой из этих видов архитектурных проблем обусловлен нефункциональными требованиями, такими как производительность; кроме того, здесь есть проблема правильности, в которой вы хотите убедиться, что обновления выполняются в правильном порядке. Итак, вам нужно подумать о рабочей нагрузке , то есть о модели использования в реальных приложениях. Имея это в виду, у вас в основном есть пара проблем: во-первых, базовые типы данных в вашем приложении могут неправильно отображаться в базу данных (например, что представляет собой свойство VARCHAR, представленное в вашем коде?), А во-вторых, ваша модель предметной области. может некорректно отображаться в вашей модели базы данных.

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

Теперь, если вы полностью контролируете модель базы данных, это несколько упрощает ситуацию, потому что тогда вы можете сделать модель базы данных более похожей на домен. Это может означать, что модель базы данных несколько денормализована, но если это так, вы можете (в зависимости от вашей базы данных) обрабатывать это с помощью представлений или просто не иметь полностью нормализованной базы данных. Нормализация - это полезная теоретическая конструкция, но это не значит, что вы не можете расслабить ее в реальной системе.

Если вы не полностью управляете своей моделью базы данных, тогда вам нужен слой объектов, которые составляют отображение. У вас есть несколько вариантов на выбор при реализации этого: вы можете создавать представления или денормализованные таблицы в базе данных, вы можете создавать промежуточные объекты, или вы можете выполнять некоторые из них, или даже иметь несколько шагов обоих (т.е. промежуточный объект, который обращается к денормализованной таблице.)

Однако в этот момент вы сталкиваетесь с проблемами «не повторяйте себя» и «делайте самое простое, что возможно будет работать». Подумайте, что может измениться? Ваша модель домена? Если у вас сильная модель предметной области, это менее вероятно - бизнес меняется относительно редко. Точное представление данных в базе данных? Чуть более распространенным. Или, чаще всего, точные шаблоны использования (например, обнаружение необходимости обрабатывать параллельные обновления). Итак, когда вы думаете об этом, что вам нужно сделать, чтобы максимально упростить работу с наиболее распространенными изменениями.

Я понимаю, что это не дает вам очень точных инструкций, но я не думаю, что мы можем предложить точные инструкции, не зная много о вашем применении. Но потом у меня также складывается впечатление, что вы задаетесь вопросом о том, каким будет «правильный» способ справиться с этим, когда вы уже работаете с чем-то, что более или менее выполняет свою работу. Итак, я бы в итоге спросил "чем ты сейчас недоволен?" и "Как бы вы хотели это решить?"

0 голосов
/ 14 февраля 2009

Я бы посмотрел на слои абстракции данных, используемые PHPCake и Symfony.

0 голосов
/ 14 февраля 2009

Многие системы используют независимый уровень данных для обработки персистентности в и из базы данных. Существует несколько моделей организации такого слоя. Некоторые используют своего рода реализацию, подобную фабрике, другие применяют взаимно-однозначное сопоставление с одним классом уровня данных на класс домена.

Модель для слоя данных часто зависит от стиля и предпочтений. Что важно, так это отделение постоянного уровня от уровня домена. Я считаю, что есть инструменты, которые помогут вам создать этот слой, но мои знания PHP невелики, поэтому я не могу назвать ни одного специально для PHP.

...