A couple of years ago, Online began an Enterprise level project with multiple vendors and a total team size of over 500 people. Oracle Fusion Middleware had been selected as the implementation technology. With 14+ years of development experience in traditional development languages such as C\C++, Delphi, C#, and Java, I approached this new technology as I would any of the other languages I’d learned. I attempted to apply SOLID principles and in doing so, realized that there was a mind shift necessary to successfully achieve this goal.
There were a few hurdles that I needed to overcome. The first couple of hurdles I knew entering the project and the third I came to realize as I attempted to implement the first. The first hurdle was how do I, or can I, apply the SOLID principles that I know and love in traditional development to a Service Oriented Architecture (SOA). The second was how do SOA principles of loose coupling, and SOLID principles co-exist if the services themselves are treated as objects? The final hurdle that I came to realize in applying the SOLID principles was that there are a lot more limitations that you need to be aware of, and work within, with this technology.
Applying SOLID Principles
SOLID is intended as principles for good object oriented programming and design to create easily maintainable and extensible applications. A SOA architecture is not, at a high level, object oriented; however, most principles can be applied with a small adjustment to our thinking of what a SOA architecture is. So how can we apply SOLID principles in our SOA architecture? Services should be viewed simply as an interface defining operations, or in object oriented terminology, methods on an object. Therefore, services are object based. If you have an xsd defining an Application Business Object (ABO), the service representing that ABO, and the operations it implements, are analogous to the methods of the object. Essentially what we are talking about here is using RESTful web services to tread the parameters into a service and the service contract itself as objects. Taking that into account, the principles can be applied as follows:
- Single responsibility for an object – This principle is applicable to ABO and service design. If the service contract is representing the methods on an object, then the service itself should only deal with a single object and that object should have a single responsibility. There's nothing different here than normal object design. The caveat is that your object definition and the methods available on that object are not stored together. The members of your object are in the scheme definition while the methods are stored as operations in a WSDL.
- Open/Closed principle – Open for extensibility, closed for modification. Unfortunately this principle is not really enforceable for SOA, but ADF can and does apply this principle – think task flows and templates. For SOA enforcement, this principle has to become an accepted best practice that, once implemented, the service can be extended, but existing operations should not be modified; instead a new operation can be defined that calls the existing operation and then adds functionality. You could take this a level lower and say no BPEL process should be modified, BUT if an operation is analogous to a method, adding a BPEL process to extend functionality in an existing operation is essentially modifying a method and would break this principle.
- Liskov substitution principle – Objects should be replaceable with instances of their subtypes. The application of this principle occurs in the ABO design and possible service contract definition. In the ADF layer this is probably easier to see implemented. However, there are a number of techniques that can be employed to allow this in a SOA architecture. The limiting factor is the lack of polymorphism in a schema. You can make your operation support a choice of object types, but this is not really ideal as it breaks other principles. A better option would be to convert type B to type A, assuming B is a subtype of A, in the web service proxy in the UI layer. This is still not ideal if B extends A adding new data members. The final option would be a wrapper service that essentially provides routing to the correct service; this provides abstraction at the UI layer and an illusion of polymorphism.
- Integration segregation principle – Many specific interfaces are better than one general interface. Obviously in a SOA architecture this principle should be followed. Break up large processes/ABO's into smaller units of work represented by separate services and/or operations.
- Dependency inversion principle – At the SOA layer, there is not any current mechanism that would allow dependency inversion where dependencies between services can be inverted.
As you can see, there are a number of restrictions represented by the Oracle Fusion Middleware as it applies to applying traditional SOLID development principles. With a combination of best practices, and a slight shift in the way services and the objects are defined, most SOLID principles can be applied.
Can SOLID and Loose Coupling be Friends in a SOA Architecture?
Coupling in general refers to the degree to which a class has direct knowledge of another class. To put this in the context of services, it is the degree to which a client has knowledge of the service. Regardless of strongly or loosely coupled, the client will need to know some information, namely the contract. If the client requires knowledge of where the service is implemented, then it would be considered strongly coupled. Use of an Enterprise Service Bus and/or UDDI lookup allows abstraction of the implementation details from the client, providing the desired loose coupling.
To return to the original question, can SOLID and loose coupling coexist? The integrated design of SOA services as objects does not imply tight coupling. Applying SOLID principles only affects the interface or contract which, in a SOA architecture, is the web service. How a client discovers a service determines the degree of coupling.