Отделенная архитектура

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

Итак, я некоторое время наблюдал за одним из выступлений Роберта С. Мартина о передовых методах, чистом коде, отделении архитектуры и т. Д., Чтобы получить некоторое вдохновение. Что я нахожу странным, так это его описание системы Fitnesse и как они реализовали методы хранения / загрузки для WikiPages. Я также связываю видео: Роберт С. Мартин - Чистая архитектура и дизайн

Что он описывает (по крайней мере, из моего понимания), так это то, что сущность знает о механизме хранения и загрузки из какого-либо постоянного уровня. Когда он хотел сохранить 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". Надеюсь, это поможет. Удачи.

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