Шаблон репозитория: что такое "правильный размер"?

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

Пользователям этого приложения необходимо отслеживать несколько типов времени для своих сотрудников. Для простоты рассмотрим только два. Я назову их "тайм-карты" и "посещаемость". Точный характер разницы между этими двумя не очень важен, но вы должны отметить, что конечные пользователи считают их полностью отдельными данными. Я думаю, однако, что причина, по которой они считают их полностью отдельными данными, заключается в том, что у них никогда не было возможности увидеть их вместе в прошлом. Оба типа записей имеют почти совершенно разные бизнес-правила, касающиеся редактирования записей, но они также, как правило, являются как отчетами о том, где сотрудник находился в определенное время. Оба типа записей времени имеют много общих свойств, таких как общее количество часов, и сотрудника, для которого было собрано время. Оба типа также имеют несколько свойств, которые полностью уникальны для отдельного типа. Мы сохраняем эти "дополнительные" свойства в экземпляре другого типа. Итак, общая структура выглядит так:

class TimeRecord 
{ 
    Person Employee { get; set; }
    TimeSpan? Hours { get; set; }
}

class TimeCardData
{
     TimeRecord Record { get; set; }
     TProperty TimeCardProperty  { get; set; }
}

class AttendanceData
{
     TimeRecord Record { get; set; }
     TProperty AttendanceProperty  { get; set; }
}

Итак, вопрос: Сколько репозиториев требуется здесь?

1 репозиторий

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

2 репозитория

В другой конструкции будет один репозиторий для "временных карт" и другого репозитория для записей "посещаемости". Это имеет то преимущество, что бизнес-правила, например, "карты времени", находятся в определенном месте. Но я также хотел бы иметь возможность получить список всех записей времени, независимо от типа. Непонятно, какой репозиторий использовать для этого случая. Оба?

3 репозитория

Также существует возможность создания одного хранилища для "временных карт", другого репозитория для записей "посещаемости" и третьего репозитория для предоставления списка только для чтения всех записей времени. Как и дизайн 2-го репозитория, это имеет то преимущество, что бизнес-правила, например, "карты времени", сами по себе. Теперь ясно, где можно получить объединенный список. Но мне кажется странным, что я могу получить одну и ту же запись из двух разных репозиториев.

Hybrid

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

Другое

Все, что я пропустил? Любые убедительные аргументы для одного проекта над другим?

Ответы

Ответ 1

  • Хранилища - это, по крайней мере, до моего сведения, место для бизнес-правил. Это просто фасад, призванный имитировать коллекцию; под ними в основном чистый доступ к данным (если это работа, вы, возможно, не сохраняете ничего с репозиторием). Поэтому отдельные репозитории не следует рассматривать по причинам "бизнес-правил".

  • Если ваши объекты домена являются действительно отдельными объектами, тогда у вас должны быть отдельные репозитории. Помните, что такое репозиторий: это фасад. Он имитирует коллекцию в вашем домене. См. Здесь, чтобы получить действительно хорошее сообщение в блоге в репозиториях: http://devlicio.us/blogs/casey/archive/2009/02/20/ddd-the-repository-pattern.aspx

Репозиторий - это фасад; абстракция.

Это говорит... Я не думаю, что у вас есть отдельные объекты. У вас есть некоторые проблемы здесь, которые не имеют ничего общего с репозиториями и все, что связано с доменом и дизайном домена. Являются ли два типа "timecards" фактически двумя разными вещами, или они действительно одинаковы?

Вы говорите: "Но мне кажется странным, что я могу получить одну и ту же запись из двух разных репозиториев".

Это говорит мне, что они на самом деле одни и те же данные, выраженные по-разному. И есть способы справиться с этим.

Если это действительно так, то то, что у вас есть, - это подклассы общего базового класса (что-то, что может быть смоделировано в БД, довольно легко и удобно обрабатывается с помощью NHibernate, например).

Я приведу вам пример проекта, над которым я работаю. У меня что-то называется "Трансляция". Это базовый класс; Абстрактные. Невозможно создать экземпляр. У меня есть два конкретных конкретных типа этого класса: DeviceBroadcast и FileBroadcast. Один поток аудио/видео с устройства (например, карта захвата DirectX) и один поток аудио/видео из источника файла (например,.mp3).

У меня есть один репозиторий, который возвращает объект Broadcast. Я могу передать его в FileBroadcast, чтобы манипулировать конкретной информацией о FileBroadcast, или я могу применить к DeviceBroadcast по той же причине - если он имеет этот тип. Широковещательная передача не может быть как FileBroadcast, так и DeviceBroadcast. Он должен быть тем или иным.

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

Базовый класс Broadcast имеет общие методы, которые используют оба подкласса (например, метод GetCommand(), который возвращает конкретный аргумент командной строки для запуска процесса VLC). Подклассы должны переопределять и реализовывать этот метод, потому что он абстрактный. Таким образом, "бизнес-логика", уникальная для FileBroadcast, содержится в классе FileBroadcast. "Бизнес-логика", уникальная для DeviceBroadcast, содержится в классе DeviceBroadcast. Любая логика, общая для обоих, содержится в суперклассе Broadcast.

У вас, похоже, есть сходная ситуация и почему я делюсь своим дизайном. Я думаю, это может послужить вам хорошо.

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