Определить ответственность за сохранение настроек (контроллер, сервисы и мапперы)

РЕДАКТИРОВАТЬ:

Потому что я опоздал с начальным награждением 300 к @arcain я снова открываюсь. И присуждение дополнительных 150 в @arcain. Если, конечно, кто-то не даст даже лучшего ответа.:)

/ РЕДАКТИРОВАТЬ

Рассмотрим следующую форму:

language | region | active | default |
-----------------------------------------------
en       | GB     | [x]    | (*)     | [X delete]
nl       | NL     | [x]    | ( )     | [X delete]
nl       | BE     | [x]    | ( )     | [X delete]

[x] let visitors browser-settings determine the default language

[save]

Настройки приведенной выше таблицы будут сохранены в таблице БД, столбцы которой соответствуют указанным выше столбцам (за исключением, очевидно, последнего столбца).

Все действия (сохранение и удаление) направляются на контроллер локализации. Контроллер Localization в основном вызывает методы для LocalizationService, например так:

$localizationService->updateCollection( $_POST ) // update collection settings
// or
$localizationService->delete( $_POST ) // delete a single locale

Сервис LocalizationService, в свою очередь, вызывает LocaleMapperDb, что-то вроде этого:

foreach( $localeCollection as $locale )
{
    $localeMapperDb->update( LocaleModel $locale );
}
// or
$localeMapperDb->delete( LocaleModel $locale );

Где, однако, ответственность за сохранение этой настройки:

[x] let visitors browser-settings determine default language

Он будет сохранен в таблице БД под названием site_settings. Я подумал о нескольких вариантах:

  • Используйте SiteService / SiteSettingsService в LocalizationController. Но затем полная форма создается и обрабатывается в LocalizationService уже.
  • Используйте SiteMapperDb / SiteSettingsMapperDb в LocalizationService и используйте его в updateCollection( $_POST)
  • Используйте SiteMapperDb / SiteSettingsMapperDb в LocaleMapperDb

Первый и последний варианты выглядят как худшие, но я не уверен. Что вы считаете лучшим вариантом? Или, может быть, у вас есть альтернативный вариант, я не подумал?

1 ответ

Решение

Я думаю, что проецирование объектов модели предметной области на объекты модели представления хорошо работает в этой ситуации.

В случае с прикрепленным кодом (прошу прощения, что написал его на C#; он должен быть достаточно переносимым), объекты модели домена никогда не открываются (доступ к ним осуществляется только непосредственно внутри объектов службы.) Сервисы предоставляют только объекты модели представления, лайк LocalViewModelи этими объектами модели представления управляют контроллеры.

LocaleConfigController также отображает данные, возвращаемые службами в LocaleConfigViewModel объект, и этот объект является единственным объектом, который обменивается непосредственно с представлением.

Итак, в двух словах, представление имеет выделенный контроллер, и представление связывается с контроллером через LocaleConfigViewModel объект. Контроллер манипулирует LocaleConfigViewModel объект и вызывает в реализации ILocaleConfigService и ISystemConfigService, Служебные объекты никогда не предоставляют модель домена контроллеру, и они отвечают за отображение объектов модели представления в объекты модели домена (с помощью любого механизма сохранения).

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

Например, на стороне управления приложением вы хотели бы использовать как службу конфигурации локализации, так и службу рендеринга строки локализации (поскольку сайт управления также может быть локализован). Для клиентского интерфейса пользователь, скорее всего, захочет только сервис рендеринга строки локализации, потому что изменения конфигурации системы должны быть нежелательными и выходить за рамки этого сайта.

Итак, чтобы окончательно ответить на ваш вопрос: контроллер содержит ссылки как на локаль, так и на службы конфигурации системы, а контроллер выделен для представления - у него есть четко определенный контракт, в котором только LocaleConfigViewModelс обменены.

Что касается ответственности за сохранение общесистемных настроек, контроллер отвечает за распаковку системных настроек из LocaleConfigViewModel и подталкивая их в соответствующие службы (в этом случае ISystemConfigService экземпляр), где они будут сохранены.

class LocaleViewModel
{
  public int Id;
  public string Language;
  public string Region;
  public bool Enabled;
  public bool Deleted;
}

class LocaleConfigViewModel
{
  public bool UseVisitorBrowserLocale;
  public LocaleViewModel DefaultLocale;
  public List<LocaleViewModel> Locales; 
}

class LocaleConfigController : ILocaleConfigController
{
  ILocaleConfigService localeConfig;
  ISystemConfigService systemConfig;

  public void Save(LocaleConfigViewModel model)
  {
    foreach (var locale in model.Locales)
    {
      if (locale.Deleted)
      {
        localeConfig.DeleteLocale(locale);
        continue;
      }
      localeConfig.UpdateLocale(locale);
    }
    systemConfig.DefaultLocaleId = model.DefaultLocale.Id;
    systemConfig.UseVisitorBrowserLocale = model.UseVisitorBrowserLocale;
  }

  public LocaleConfigViewModel GetCurrentView()
  {
    var model = new LocaleConfigViewModel();
    model.Locales = localeConfig.Locales;
    model.DefaultLocale = model.Locales.FirstOrDefault(l => l.Id == systemConfig.DefaultLocaleId);
    model.UseVisitorBrowserLocale = systemConfig.UseVisitorBrowserLocale;
    return model;
  }

  // ...
}

interface ILocaleConfigController
{
  void Save(LocaleConfigViewModel model);
  LocaleConfigViewModel GetCurrentView();
  // ... 
}

interface ILocaleConfigService // services will be stateless and threadsafe
{
  void DeleteLocale(LocaleViewModel locale);
  void UpdateLocale(LocaleViewModel locale);
  List<LocaleViewModel> Locales { get; }
  // ...
}

interface ISystemConfigService // services will be stateless and threadsafe
{
  int DefaultLocaleId { get; set; }
  bool UseVisitorBrowserLocale { get; set; }
  // ...
}
Другие вопросы по тегам