Отделенная архитектура
Я работаю над системой, в которой я хотел бы максимально разделить свои слои, вы знаете, какое-то модульное приложение, чтобы иметь возможность переключать базы данных и все такое без серьезной модификации остальной системы.
Итак, я некоторое время наблюдал за одним из выступлений Роберта С. Мартина о передовых методах, чистом коде, отделении архитектуры и т. Д., Чтобы получить некоторое вдохновение. Что я нахожу странным, так это его описание системы Fitnesse
и как они реализовали методы хранения / загрузки для WikiPage
s. Я также связываю видео: Роберт С. Мартин - Чистая архитектура и дизайн
Что он описывает (по крайней мере, из моего понимания), так это то, что сущность знает о механизме хранения и загрузки из какого-либо постоянного уровня. Когда он хотел сохранить WikiPages в памяти, он просто переопределил WikiPage и создал новый InMemoryWikiPage
, Когда он хотел сохранить их в базе данных, он сделал то же самое...
Итак, один из моих вопросов - как называется этот подход? Я все время изучал шаблоны и прочее в репозитории, и почему такие классы должны быть такими же невежественными, но, похоже, я не могу найти никаких материалов по этой вещи, которую он сделал. Поскольку мое приложение будет состоять из модулей, я думаю, что это может помочь решить мои проблемы без необходимости создания какого-либо централизованного хранилища для моих сущностей... Каждый модуль просто позаботится о себе, включая сохранение своих сущностей.
Я думаю, что код будет выглядеть примерно так:
public class Person : IEntity
{
public int ID { get;set; }
public string Name { get;set; }
public void Save()
{
..
}
public void Update()
{
}
public void Delete()
{
}
...
}
Кажется немного странным, но... Или, может быть, я неправильно понял, что он сказал в видео?
Мой второй вопрос: если вы не согласны с этим подходом, какой путь вы бы выбрали в таком модульном приложении?
Пожалуйста, приведите пример, если возможно, с некоторыми пояснениями.
2 ответа
Размещенный вами шаблон является активной записью.
Различие между репозиторием и шаблоном активной записи состоит в том, что в шаблоне активной записи запрос и постоянство данных и объект домена находятся в одном классе, где, как и в репозитории, постоянство данных и запрос отделены от самого объекта домена.
Другим шаблоном, который вы, возможно, захотите изучить, является объект Query, который, в отличие от шаблона репозитория, в котором число методов будет увеличиваться в каждом возможном запросе (фильтр, сортировка, группировка и т. Д.), Объект запроса может использовать свободный интерфейс для выразительности [1] или посвященный, в котором вы можете передать параметр [2]
Наконец, вы можете взглянуть на архитектуру разделения ответственности командного запроса для идей. Я лично свободно следил за этим, просто подбирал идеи, которые могут мне помочь.
Надеюсь это поможет.
Обновите базу по комментарию
Одним из вариантов шаблона репозитория является это
UserRepository
{
IEnumerable<User> GetAllUsers()
IEnumerable<User> GetAllByStatus(Status status)
User GetUserById(int id)
...
}
Этот не масштабируется, так как хранилище обновляется для дополнительного запроса таким образом, чтобы запрашивать
Другой вариант - передать объект запроса в качестве параметра в запрос данных.
UserRepository
{
IEnumerable<User> GetAll(QueryObject)
User GetUserById(int id)
...
}
var query = new UserQueryObject(status: Status.Single)
var singleUsers = userRepo.GetAll(query)
Некоторые в мире.Net, выражение Linq передается вместо Query Object
var singleUsers = userRepo.GetAll(user => user.Status == Status.Single)
Другой вариант заключается в том, чтобы выделить репозиторий для извлечения из одного объекта по его уникальному идентификатору и сохранить его, в то время как объект запроса используется для отправки извлечения данных, как в CQRS.
Обновление 2
Я предлагаю вам ознакомиться с принципами SOLID. Эти принципы очень полезны при создании слабосвязанной, высокосвязанной архитектуры.
Сборник Los Techies по ценообразованию SOLID содержит хорошие вводные статьи, касающиеся ценообразования SOLID.
Я отвечу на ваш второй вопрос. Я думаю, что вы будете заинтересованы в Dependency Injection
,
Я не эксперт по DI, но я постараюсь объяснить как можно яснее.
Во-первых, из Википедии:
Внедрение зависимостей - это шаблон проектирования программного обеспечения, который позволяет удалять жестко запрограммированные зависимости и позволяет изменять их как во время выполнения, так и во время компиляции.
Основная цель шаблона внедрения зависимостей состоит в том, чтобы сделать возможным выбор из нескольких реализаций данного интерфейса зависимостей во время выполнения или через файлы конфигурации, а не во время компиляции.
Есть много библиотек, которые помогают вам реализовать этот шаблон проектирования: AutoFac, SimpleInjector, Ninject, Spring .NET и многие другие.
Теоретически, так будет выглядеть ваш код (пример AutoFac)
var containerBuilder = new ContainerBuilder();
//This is your container builder. It will be used to register interfaces
// with concrete implementations
Затем вы регистрируете конкретные реализации для типов интерфейса:
containerBuilder.RegisterType<MockDatabase>().As<IDatabase>().InstancePerDependency();
containerBuilder.RegisterType<Person>().As<IPerson>().InstancePerDependency();
В этом случае, InstancePerDependency
означает, что всякий раз, когда вы пытаетесь решить IPerson
, вы получите новый экземпляр. Это может быть, например, SingleInstance
поэтому всякий раз, когда вы пытались решить IPerson
, вы получите тот же общий экземпляр.
Затем вы создаете свой контейнер и используете его:
var container = containerBuilder.Build();
IPerson myPerson = container.Resolve<IPerson>(); //This will retrieve the object based on whatever implementation you registered for IPerson
myPerson.Id = 1;
myPerson.Save(); //Save your changes
Модель, которую я использовал в этом примере:
interface IEntity
{
int Id { get; set; }
string TableName { get; }
//etc
}
interface IPerson: IEntity
{
void Save();
}
interface IDatabase
{
void Save(IEntity entity);
}
class SQLDatabase : IDatabase
{
public void Save(IEntity entity)
{
//Your sql execution (very simplified)
//yada yada INSERT INTO entity.TableName VALUES (entity.Id)
//If you use EntityFramework it will be even easier
}
}
class MockDatabase : IDatabase
{
public void Save(IEntity entity)
{
return;
}
}
class Person : IPerson
{
IDatabase _database;
public Person(IDatabase database)
{
this._database = database;
}
public void Save()
{
_database.Save(this);
}
public int Id
{
get;
set;
}
public string TableName
{
get { return "Person"; }
}
}
Не волнуйтесь, AutoFac автоматически разрешит любые Person
Зависимости, такие как IDatabase
,
Таким образом, если вы хотите переключить свою базу данных, вы можете просто сделать это:
containerBuilder.RegisterType<SqlDatabase>().As<IDatabase>().InstancePerDependency();
Я написал слишком упрощенный (не пригодный для использования) код, который служит просто кикстартом, для получения дополнительной информации обратитесь к Google "Dependency Injection". Надеюсь, это поможет. Удачи.