我们将核心Web应用程序重构为3层SOA架构:
大约有5个内部网站,每个网站都有自己的服务层。 服务和UI通过DTO在彼此之间共享信息。 问题是,大多数服务方法都是专门为UI设计的,因此有些服务变得非常庞大,例如:
// 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
我们最终得到了大量的获取方法,其中实际的逻辑方法正在丢失。 是否有更好的命名约定,甚至是完全不同的方法?(不暴露域/持久性) Web /服务层是物理隔离的,因此必须使用WCF。
答案 0 :(得分:1)
我喜欢将过滤器传递给一般的get问题,就像那样
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本身),但是这意味着您将查询整个数据库而不是在其中进行过滤
答案 1 :(得分:0)
samy提议的变体如下:
您可以像这样表达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,这将导致在服务端的异常中无法实例化抽象类(因为服务知道它的抽象)。