在WCF服务中重构God对象

Gio*_*uri 9 wcf soa servicecontract

我们god object在我们的系统中遇到了一个.该系统包括public service暴露给我们的客户,middle office serviceback office service.

流程如下:用户注册一些交易public service,然后经理middle office service检查交易并批准或拒绝交易,最后管理者back office service完成或拒绝交易.

使用我'这个词transaction,但实际上这些都是不同的类型,如操作的CRUD on entity1,CRUD on entiny2......不仅CRUD操作,但其他许多操作,例如approve/send/decline entity1,make entity1 parent/child of entity2等等...

现在WCF服务合同只是根据系统的那些部分分开.所以我们有3个服务合同:

PublicService.cs
MiddleOfficeService.cs
BackOfficeService.cs
Run Code Online (Sandbox Code Playgroud)

每个都有大量的运营合同:

public interface IBackOfficeService
{
    [OperationContract]
    void AddEntity1(Entity1 item);

    [OperationContract]
    void DeleteEntity1(Entity1 item);

    ....

    [OperationContract]
    void SendEntity2(Entity2 item);

    ....
}
Run Code Online (Sandbox Code Playgroud)

所有3项服务的运营合同数量已经达到2000个,每个服务合同约为600个.它不仅打破了最佳实践,而且只需更新服务引用就会非常痛苦,因为它需要很长时间.并且系统每天都在增长,并且在每次迭代中将越来越多的操作添加到这些服务中.

现在我们面临两难困境,因为我们如何将这些神服务分解为逻辑部分.有人说服务不应包含超过12~20次操作.其他人说一些不同的事情.我意识到没有黄金法则,但我希望听到一些关于此的建议.

例如,如果我只是按实体类型拆分这些服务,那么我可以在项目中获得大约50个服务端点和50个服务引用.在这种情况下,可维护性是什么?

还有一件事需要考虑.假设我选择了按实体拆分这些服务的方法.例如:

public interface IEntity1Service
{
    [OperationContract]
    void AddEntity1(Entity1 item);

    [OperationContract]
    void ApproveEntity1(Entity1 item);

    [OperationContract]
    void SendEntity1(Entity1 item);

    [OperationContract]
    void DeleteEntity1(Entity1 item);
    ....

    [OperationContract]
    void FinalizeEntity1(Entity1 item);

    [OperationContract]
    void DeclineEntity1(Entity1 item);
}
Run Code Online (Sandbox Code Playgroud)

现在发生的事情是我应该在public client和中添加对此服务的引用back office client.但back office只需要FinalizeEntity1DeclineEntity1操作.因此,这里是一个典型的侵犯Interface segregation principleSOLID.所以,我必须拆分进一步的可能是3级不同的服务IEntity1FrontService,IEntity1MiddleService,IEntity1BackService.

ken*_*n2k 5

这里的挑战是重构代码而不改变代码的大部分以避免潜在的回归.

避免使用数千行的大型业务代码的一种解决方案是将接口/实现拆分为多个部分,每个部分代表给定的业务域.

例如,您的IPublicService接口可以编写如下(使用接口继承,每个业务域一个接口):

IPublicService.cs:

[ServiceContract]
public interface IPublicService : IPublicServiceDomain1, IPublicServiceDomain2
{
}
Run Code Online (Sandbox Code Playgroud)

IPublicServiceDomain1.cs:

[ServiceContract]
public interface IPublicServiceDomain1
{
    [OperationContract]
    string GetEntity1(int value);
}
Run Code Online (Sandbox Code Playgroud)

IPublicServiceDomain2.cs:

[ServiceContract]
public interface IPublicServiceDomain2
{
    [OperationContract]
    string GetEntity2(int value);
}
Run Code Online (Sandbox Code Playgroud)

现在,对于服务实现,您可以使用部分类(每个业务域一个部分类)将其拆分为多个部分:

Service.cs:

public partial class Service : IPublicService
{
}
Run Code Online (Sandbox Code Playgroud)

Service.Domain1.cs:

public partial class Service : IPublicServiceDomain1
{
    public string GetEntity1(int value)
    {
        // Some implementation
    }
}
Run Code Online (Sandbox Code Playgroud)

Service.Domain2.cs:

public partial class Service : IPublicServiceDomain2
{
    public string GetEntity2(int value)
    {
        // Some implementation
    }
}
Run Code Online (Sandbox Code Playgroud)

对于服务器配置,仍然只有一个端点:

<system.serviceModel>
  <services>
    <service name="WcfServiceLibrary2.Service">
      <endpoint address="" binding="basicHttpBinding" contract="WcfServiceLibrary2.IPublicService">
        <identity>
          <dns value="localhost" />
        </identity>
      </endpoint>
      <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      <host>
        <baseAddresses>
          <add baseAddress="http://localhost:8733/Design_Time_Addresses/WcfServiceLibrary2/Service1/" />
        </baseAddresses>
      </host>
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior>
        <serviceMetadata httpGetEnabled="True" httpsGetEnabled="True" />
        <serviceDebug includeExceptionDetailInFaults="False" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>
Run Code Online (Sandbox Code Playgroud)

对于客户端也是如此:仍然是一个服务引用:

<system.serviceModel>
  <bindings>
    <basicHttpBinding>
      <binding name="BasicHttpBinding_IPublicService" />
    </basicHttpBinding>
  </bindings>
  <client>
    <endpoint address="http://localhost:8733/Design_Time_Addresses/WcfServiceLibrary2/Service1/"
      binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IPublicService"
      contract="ServiceReference1.IPublicService" name="BasicHttpBinding_IPublicService" />
  </client>
</system.serviceModel>
Run Code Online (Sandbox Code Playgroud)

这允许通过将大型服务拆分为多个逻辑部分(与给定业务域相关联的每个部分)来重构服务器端.

这并不会改变您的3个服务中的每个服务仍然有600个操作的事实,因此客户端代理生成仍然需要很长时间.至少你的代码将更好地组织在服务器端,并且重构将是便宜的并且没有那么危险.

这里没有银弹,这只是代码重组,以提高可读性/维护性.

200个服务,每个服务10个操作,20个服务,每个服务100个操作,这是另一个主题,但可以肯定的是,重构需要更多的时间,你仍然可以进行2000次操作.除非您重构整个应用程序并减少此数量(例如,通过提供更"高级"(并非总是可能)的服务).