Должен ли я использовать репозиторий в объекте домена или вернуть объект домена обратно на уровень обслуживания?

10

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

Класс обслуживания с именем OrganisationService, интерфейс которого содержит методы для извлечения и сохранения экземпляров объектов домена организации. Организация является совокупным корнем и имеет другие связанные с ней данные: участники и лицензии. Сначала DBContext базы данных EF6 используется в OrganisationService для извлечения сущностей OrganisationDB и связанных сущностей MemberDB и LicenseDB. Все они преобразуются в свои эквиваленты класса объекта домена, когда извлекаются OrganisationService и загружаются в объект домена организации. Этот объект выглядит так:

public class Organisation
{
    public IList<Member> Members { get; set; }
    public IList<License> Licenses { get; set; }
}

Я не использую шаблон репозитория в OrganisationService ... Я использую сам EF в качестве репозитория, так как кажется, что EF6 в значительной степени сделал репозиторий избыточным.

На данный момент в проекте объект домена Organization является анемичным: он выглядит как класс организации EF POCO. Класс OrganisationService очень похож на репозиторий!

Теперь мне нужно начать добавлять логику. Эта логика включает управление лицензиями и участниками организации. Теперь, в дни сценариев транзакций, я бы добавил методы в OrganisationService для обработки этих операций и вызвал бы репозиторий для взаимодействия с БД, но с DDD я считаю, что эта логика должна быть инкапсулирована в самом объекте домена организации ...

Вот где я не уверен, что мне следует делать: мне нужно будет сохранить эти данные обратно в базу данных как часть логики. Означает ли это, что я должен использовать DbContext внутри объекта домена организации, чтобы сделать это? Использование репозитория / EF в доменном объекте - плохая практика? Если да, то где это постоянство?

public class Organisation
{
    public IList<Member> Members { get; set; }
    public IList<License> Licenses { get; set; }

    public void AddLicensesToOrganisation(IList<License> licensesToAdd)
    {
        // Do validation/other stuff/

        // And now Save to the DB???
        using(var context = new EFContext())
        {
            context.Licenses.Add(...
        }

        // Add to the Licenses collection in Memory
        Licenses.AddRange(licensesToAdd);
    }
}

Должен ли я вместо этого просто мутировать объект домена организации в памяти, а затем отправить его обратно в OrganisationService для сохранения? Затем я должен отследить, что на самом деле изменилось на объекте (что EF делает со своими собственными POCO! Я как бы начинаю чувствовать, что EF - это не просто замена репозитория, но и уровень домена!)

Любое руководство здесь приветствуется.

MrLane
источник

Ответы:

2

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

Entity Framework определенно предназначен для замещения ваших доменных сущностей. Это хорошо работает, когда ваши доменные сущности и сущности данных являются одними и теми же классами. Намного приятнее, если вы можете положиться на EF, чтобы отслеживать изменения для вас, и просто позвонить, context.SaveChanges()когда вы закончите со своей транзакционной работой. Это также означает, что ваши атрибуты проверки не нужно устанавливать дважды, один раз для моделей вашего домена и один раз для ваших постоянных сущностей - такие вещи можно проверить [Required]или [StringLength(x)]проверить в вашей бизнес-логике , что позволяет вам перехватывать недопустимые состояния данных, прежде чем пытаться сделать транзакцию БД и получитьEntityValidationException, Наконец, это быстро для кодирования - вам не нужно писать слой отображения или репозиторий, но вместо этого вы можете напрямую работать с контекстом EF. Это уже репозиторий и единица работы, поэтому дополнительные уровни абстракции ничего не делают.

Недостатком объединения вашего домена и постоянных сущностей является то, что в итоге вы получите набор [NotMapped]атрибутов, разбросанных по всем вашим свойствам. Часто вам понадобятся специфичные для домена свойства, которые либо являются фильтрами только для получения постоянных данных, либо задаются позже в вашей бизнес-логике и не сохраняются обратно в вашу базу данных. Иногда вы захотите выразить свои данные в моделях вашего домена таким образом, который не очень хорошо работает с базой данных - например, при использовании перечислений Entity отобразит их в intстолбец - но, возможно, вы захотите отобразить они удобочитаемы string, так что вам не нужно обращаться к поиску при проверке базы данных. Затем вы получите stringсвойство, которое отображается,enumсвойство, которое не является (но получает строку и отображается на перечисление), и API, который предоставляет оба! Точно так же, если вы хотите объединить сложные типы (таблицы) в разных контекстах, вы можете получить mappedOtherTableId и несопоставленное ComplexTypeсвойство, оба из которых отображаются как открытые для вашего класса. Это может сбить с толку тех, кто не знаком с проектом, и излишне раздувает ваши доменные модели.

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

Одним из способов избежать ручного отслеживания изменений вашей сущности является сохранение соответствующего постоянного идентификатора сущности в вашей доменной модели. Это может быть заполнено автоматически вашим картографическим слоем. Затем, когда вам необходимо сохранить изменение обратно в EF, извлеките соответствующий постоянный объект перед выполнением любого отображения. Затем, когда вы отобразите изменения, EF обнаружит их автоматически, и вы сможете позвонить, context.SaveChanges()не отслеживая их вручную.

public class OrganisationService
{
    public void PersistLicenses(IList<DomainLicenses> licenses) 
    {
        using (var context = new EFContext()) 
        {
            foreach (DomainLicense license in licenses) 
            {
                var persistedLicense = context.Licenses.Find(pl => pl.Id == license.PersistedId);
                MappingService.Map(persistedLicense, license); //Right-left mapping
                context.Update(persistedLicense);
            }
            context.SaveChanges();
        }
    }
}
NWard
источник
Есть ли проблема при объединении разных подходов с различной сложностью данных? если у вас есть что-то, что красиво отображается в БД, просто используйте EF напрямую. Если у вас есть что-то, чего нет, тогда представьте отображение / абстракцию перед использованием EF.
Эйфорический
@Euphoric различия доменов и БД могут быть обработаны в отображении. Я не могу вспомнить случай использования, когда добавление домена дважды (непостоянное и постоянное) и ручное отслеживание изменений - это меньше работы, чем добавление сопоставлений для особых случаев. Можете ли вы указать мне один? (Я предполагаю, что используя NHibernate, может быть, EF более ограничен?)
Firo
Очень полезный, практичный и информативный ответ. Маркировка как ответ. Спасибо!
MrLane
3

я не знаю EF6, но другие ORM обрабатывают это прозрачно, поэтому вам не нужно явно добавлять лицензии в контекст, он обнаружит их при сохранении изменений. Таким образом, метод будет до

public void AddLicenses(IEnumerable<License> licensesToAdd)
{
    // Do validation/other stuff/
    // Add to the Licenses collection in Memory
    Licenses.AddRange(licensesToAdd);
}

и код вокруг него

var someOrg = Load(orgId);

someOrg.AddLicenses(someLicences);

SaveChanges();
Firo
источник
Мои доменные объекты не являются сущностями, поэтому добавление их в коллекцию Licenses, которая является просто списком, не приведет к ее сокращению. Вопрос не столько в EF, сколько в том, где происходит фактическая настойчивость. Все постоянство в EF должно происходить в контексте контекста. Вопрос в том, к чему это относится?
MrLane
2
сущности домена и постоянные сущности могут / должны быть одинаковыми, в противном случае вы вводите другой уровень абстракции, который в 99% случаев не добавляет ценности, и отслеживание изменений теряется.
Firo