Найдите шаблон проектирования для управления операцией, которая требует обновления данных в нескольких источниках данных. - PullRequest
0 голосов
/ 18 мая 2018

У меня есть API с почтовой операцией.Мы используем C # и имеем отдельные сборки для каждого слоя (Api, Business Logic, Access Data)

Эта операция должна взаимодействовать с несколькими источниками данных.

  1. Проверьте, существуют ли данные ужев источнике данных 1
  2. Если он не существует в источнике данных 1, проверьте, существуют ли данные в источнике данных 2
  3. Если он не существует в источнике данных 2, вставьте его в источник данных 1и затем вставьте его в источник данных 2

Мы регулярно используем службы (для логики) и репозитории для доступа к данным.

Опция 1

  • Создайте репозиторий для каждого источника данных, каждый с операцией get и операцией вставки.
  • Создайте службу, которая использует оба репозитория и следует приведенной выше логике.

Преимущество состоит в том, что вся логика в обслуживании, и она чувствует себя немного более «Единой ответственностью».Недостатком этого является то, что кто-то позднее сможет использовать «хранилище источника данных 1» напрямую и вставить запись, не зная, что ее следует также вставить в источник данных 2.

Опция 2 Создайте один репозиторий с единственной операцией получения и вставки, которая взаимодействует с обоими источниками данных.Это гарантировало бы, что вы не можете взаимодействовать с одним без другого, но чувствует себя гораздо менее «Единой ответственностью».

Опция 3 Есть 3 хранилища ....

  • 1 Хранилище для источника данных 2 (но имеет только внутренний интерфейс, поэтому не может использоваться другимсборка)
  • 1 репозиторий с общедоступным интерфейсом и может использоваться службой (в другой сборке)

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

Что думают люди?Есть ли шаблон дизайна для этого?

Ответы [ 4 ]

0 голосов
/ 20 мая 2018

Из того, что я вижу, интерфейс выглядит как persistSomethingToRepository.Этот интерфейс полностью заботит клиентский код.Это не заботится о том, что вы используете вторичную унаследованную систему (и не должно).Это наблюдение значительно упрощает реализацию, поскольку вы можете использовать шаблон Decorator для украшения DataSource1 с сохранением в DataSource2 также.

У меня (пока) недостаточно опыта в C #, но псевдо-ООП-код выглядит примерно так:

interface Repository {
    void persistSomething(something);
}

class Source1 implements Repository {
    void persistSomething(something) {
        // insert into database
   }
}

class Source2AndSource1 implements Repository {
    // inject the right database connection and a Source1 instance
    void persistSomething(something) {
        // Check if the data already exists in datasource 1
        // If it doesn't exist in datasource 1, check if the data already exists in datasource  2
       //If it doesn't exist in datasource 2, insert it in to datasource 1 and then insert it in to datasource 2
   }
}

Затем вы просто настраиваете свой контейнер для инъекций зависимости (DIC) для использованияэкземпляр Source2AndSource1, когда требуется Repository.

Когда ваша устаревшая система (источник 2) больше не используется, вы просто настраиваете DIC для использования экземпляра Source1 вместо Source2AndSource1без каких-либо изменений в коде клиента.Это дает вам SOLID код, точнее открытый / закрытый принцип и принцип единоличной ответственности .

0 голосов
/ 19 мая 2018

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

enter image description here

Итак, я полагаю, мы можем уменьшитьэто так.

enter image description here

И иметь только один действительный источник данных.

Кроме того, рассмотрите механизм синхронизации, если вам нужно,что может привести к накладным расходам.

0 голосов
/ 19 мая 2018

С одной стороны, я мог бы создать хранилище для каждого источника данных, каждый с операцией get и операцией вставки.Тогда я мог бы создать сервис, который следует приведенной выше логике.

Преимущество состоит в том, что вся логика в сервисе, и он чувствует себя немного более "Единой ответственностью".

Мне тоже нравится эта опция, поэтому на вашем месте я бы выбрал эту опцию.

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

Мне не нравится этот вариант размещения проверки источников данных в одном репо, потому что это звучит какответственность сервисного уровня.

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

Я не уверен, что вы подразумеваете под внутренним.Если под внутренним вы имеете в виду internal как внутри сборки, он по-прежнему имеет тот же недостаток, что и предыдущий параметр.

О первом варианте, который вам и вам нравится, вы говорите так:

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

Мы можем сделать что-то, чтобы избежать этого.Создайте такой класс и представьте, что мы сохраняем Customer:

public class Customer { }

public class CustomerData
{
    public string MyProperty { get; private set; }

    private CustomerData()
    {
    }

    public Customer Customer { get; private set; }

    // or not void
    public static void Save(Customer customer)
    {
        // Check all datasources and save wherever you need to 
        // obviously through injection or whatever. I will just
        // new-it up here
        var cd = new CustomerData { Customer = customer };
        var repo1 = new CustomerRepository();
        repo1.Save(cd);

        bool someCondition = true;
        if (someCondition)
        {
            var repo2 = new CustomerRepository();
            repo2.Save(cd);
        }
    }
}

Обратите внимание, что конструктор является закрытым, поэтому разработчики не могут его создать.Теперь создайте свой репозиторий следующим образом:

public class CustomerRepository
{
    public void Save(CustomerData cd) { // Save cd.Customer to datasource }
}

Обратите внимание, что в качестве аргумента принимается CustomerData.

ОК!Итак, давайте используем репо:

var repo = new CustomerRepository();
// Oops cannot create CustomerData. 
// repo has no other method except for CustomerData.
repo.Save(new CustomerData()); 

// There is no other way except for this or the developer has to add 
// a method to the repo-but what can we do about that, not much!
CustomerData.Save(new Customer());
0 голосов
/ 18 мая 2018

Требование во многом похоже на шаблон цепочки ответственности , к сожалению, это не поможет уменьшить количество кода доступа к данным.Я бы испытал желание иметь читателя на «уровень» в проверках, заставить их всех реализовать какой-то общий интерфейс и использовать цепочку ответственности (т.е. могу ли я справиться с этим, если не попросить следующий объект в цепочке) сделатьоперации.Вы можете сделать каждый из элементов в цепочке мини-хранилищем или использовать другое хранилище, если требуется.

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

...