Решение для услуг раздутых с помощью методов получения?

1

Мы реорганизуем наши основные веб-приложения в 3-уровневую SOA-архитектуру:

  • презентация (asp mvc)
  • сервисный уровень (wcf, net.tcp)
  • настойчивость (sql).

Существует около 5 внутренних веб-сайтов, каждый из которых имеет свой собственный сервисный уровень. Сервисы и пользовательский интерфейс обмениваются информацией между собой через DTO. Проблема в том, что большинство методов обслуживания создаются специально для пользовательского интерфейса, поэтому некоторые службы становятся очень громоздкими, например:

// CompanyService:
CompanyDTO GetByName(String)  
CompanyDTO GetByNameAndCity(String, String)
CompanyDTO GetByNameOrCity(String, String)
CompanyDTO GetByStartsWithNameOrStartsWithCity (String, String)
CompanyDTO GetByNameOrStartsWithCity(String, String)
CompanyDTO GetByNameOrStartsWithCityAndHavingAtLeastOneUser(String, String)
...
CompanyPeopleDTO GetByNameWithPeople(String)
CompanyPeopleDTO GetByNameAndCityWithPeople(String,String)
CompanyPeopleActivityDTO GetByNameWithPeopleAndActivites(String)
...

и иногда есть некоторые очень специфические запросы, например:

CompanyAdmin1DTO GetComplexForAdmins1(String, String, String, String, Boolean, Boolean, int) // name,city,country,email, is deleted, is active, founded
CompanyAdmin2DTO GetComplexForAdmins2(String, String, String, Boolean) // name starts with, city starts with, have users
CompanyAdmin3DTO GetComplexForAdmins3(String, String, String, int) // name OR city AND have users

У нас заканчивается тонна методов извлечения, где фактические логические методы теряются. Существуют ли лучшие соглашения об именах для этого или даже совершенно другого подхода? (без раскрытия домена/персистентности). Уровни веб-сервисов физически разделены, поэтому WCF является обязательным.

  • 0
    Могут быть альтернативы, но я понимаю, что это фасадные методы и, возможно, это кажется беспорядком ... но в конце концов, фасады пытаются решить сложность при использовании основного домена ..
Теги:
web-services
wcf
soa

2 ответа

1

Я поклонник передачи фильтров на общий вопрос, что-то вроде этого

IEnumerable<stuff> GetTheStuff(params Func<stuff, bool> filters)
{
    IQueryable<stuff> resultList = DAL.Stuff.Queryable;
    foreach (var filter in filters)
    {
        resultList = resultList.Where(filter);
    }
}

(синтаксис приблизительный, у меня нет IDE)

Преимущество состоит в том, что вы можете комбинировать любой фильтр в вызове, чтобы заставить его делать то, что вам нужно: запрос остается явным, и вы не умираете от перегрузки сигнатур Get*

GetStuff(stuff => stuff.Name.Contains("great stuff"), stuff => stuff.CreationDate.Year == 1900 );

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

var NamedGreat = stuff => stuff.Name.Contains("great stuff");
var CreatedIn1900 = stuff => stuff.CreationDate.Year == 1900;

GetStuff(NamedGreat, CreatedIn1900);

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


Это предоставляет часть домена, но вы можете создавать фильтры, которые могут быть выражены против DTO (например). Обратите внимание, не применяя фильтры к перераспределенным спискам объектов (например, сами DTO), поскольку это подразумевает, что вы запрашиваете всю БД вместо фильтрации в ней

  • 0
    Домен внутри сервисов изолирован и поэтому не доступен для пользовательского интерфейса. Это будет прекрасно работать внутри сервисного уровня.
  • 0
    И вы можете выразить свои фильтры в качестве аргументов, см. Редактировать
0

Вариант того, что предложил, был следующим:

Вы можете выразить операцию GetCompany следующим образом:

CompanyDTO GetCompany(CompanyQueryBase query)

где CompanyQueryBase является абстрактным классом, из которого следуют следующие данные:

CompanyNameQuery
CompanyNameAndCityQuery
CompanyNameOrCityQuery
CompanyStartsWithNameOrStartsWithCityQuery
CompanyNameOrStartsWithCityQuery
CompanyNameOrStartsWithCityAndAtLeastOneUserQuery

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

CompanyDTO GetCompany(CompanyQueryBase query)
{
   // _queryHandlerFactory is a member of the service class
   // it is best to use DI for injecting the specific query handler factory
   // as a dependency to the class
   var queryHandler = _queryHandlerFactory.GetHander(query);
   return queryHandler.Execute();
}

Это позволит вам сохранить вашу текущую логику в основном нетронутой, выставить только один метод Get (для компании) и легко расширяться для будущих запросов, которые вам нужно будет поддерживать.

Однако с этим решением существует довольно неприятная проблема. Ваши клиенты не будут знать, что CompanyQueryBase является абстрактным классом (из-за того, что DataContractSerializer не поддерживает объявления типа сложного типа xsd, хотя, как ни странно, XmlSerializer делает это), что означает, что ваш клиент сможет случайно отправить вам CompanyQueryBase, что приведет к в исключении на стороне обслуживания, которое не может создать экземпляр абстрактного класса (потому что служба знает его реферат).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню