Как обрабатывать связь между доменом и уровнями базы данных?

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

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

Например:

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

4 ответа

Решение

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

Обновить

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

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

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

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

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

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

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

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

Учтите следующее.

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

Что касается связи между доменом и постоянными уровнями, рассмотрим их как две совершенно разные системы, которые не связаны друг с другом. Все они знают, каковы его обязанности и какие объявления он делает. Например, уровень постоянства знает, как сохранить данные, переданные ему, и объявить, что данные были сохранены. Однако постоянный уровень не обязательно должен понимать доменные объекты. Аналогично, доменный слой понимает 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,

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

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

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

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

Другие вопросы по тегам