soa - Refactoring God objects in WCF services -
we came across god object
in our system. system consists of public service
exposed our clients, middle office service
, back office service
.
the flow following: user registers transaction in public service
, manager middle office service
checks transaction , approves or declines transaction , manager back office service
finalizes or declines transaction.
i'am using word transaction
, in reality different types of operations crud on entity1
, crud on entiny2
... not crud
operations many other operations approve/send/decline entity1
, make entity1 parent/child of entity2
etc etc...
now wcf
service contracts separated according parts of system. have 3 service contracts:
publicservice.cs middleofficeservice.cs backofficeservice.cs
and huge amount of operation contracts in each:
public interface ibackofficeservice { [operationcontract] void addentity1(entity1 item); [operationcontract] void deleteentity1(entity1 item); .... [operationcontract] void sendentity2(entity2 item); .... }
the number of operation contracts 2000 across 3 services , approximately 600 per each service contract. not breaking best practices, huge pain update service references takes ages. , system growing each day , more , more operations added services in each iteration.
and facing dilemma how can split god services logical parts. 1 says service should not contain more 12~20 operations. others different things. realize there no golden rule, wish hear recommendations this.
for example if split services per entity type can 50 service endpoints , 50 service reference in projects. maintainability in case?
one more thing consider. suppose choose approach split services per entity. example:
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); }
now happens should add reference service both in public client
, back office client
. back office
needs finalizeentity1
, declineentity1
operations. here classic violation of interface segregation principle
in solid
. have split further may 3 distinct services ientity1frontservice
, ientity1middleservice
, ientity1backservice
.
the challenge here refactor code without changing large portions of avoid potential regressions.
one solution avoid large business code thousands of lines split interfaces/implementations multiple parts, each part representing given business domain.
for instance, ipublicservice
interface written follows (using interface inheritance, one interface each business domain):
ipublicservice.cs
:
[servicecontract] public interface ipublicservice : ipublicservicedomain1, ipublicservicedomain2 { }
ipublicservicedomain1.cs
:
[servicecontract] public interface ipublicservicedomain1 { [operationcontract] string getentity1(int value); }
ipublicservicedomain2.cs
:
[servicecontract] public interface ipublicservicedomain2 { [operationcontract] string getentity2(int value); }
now service implementation, split multiple parts using partial classes (one partial class each business domain):
service.cs
:
public partial class service : ipublicservice { }
service.domain1.cs
:
public partial class service : ipublicservicedomain1 { public string getentity1(int value) { // implementation } }
service.domain2.cs
:
public partial class service : ipublicservicedomain2 { public string getentity2(int value) { // implementation } }
for server configuration, there still 1 endpoint:
<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>
same client: still 1 service reference:
<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>
this allows refactor server side splitting huge services multiple logical parts (each part associated given business domain).
this doesn't change fact each of 3 services still has 600 operations, client proxy generation still take ages. @ least code better organized server-side, , refactoring cheap , not-so-risky.
there no silver-bullet here, code reorganization better readability/maintenance.
200 services 10 operations each vs 20 services 100 operations each topic, sure refactoring require way more time, and still have 2000 operations. unless refactor whole application , reduce number (for instance providing services more "high-level" (not possible)).
Comments
Post a Comment