Существует различие между моделью предметной области и ее реализацией. То, что ваша модель показывает отношение 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
.
Извините за длинный ответ ...