One of common tasks in software development is building a client library for some service API. Over the years I've come up with a way of structuring these libraries, which has served me well. The evolution of my approach went all the way from 2 layer (Business - Client) to now 4 layers. In some specific cases you might need more than these 4, but in usual case it enough. So the layers are:
- Business/Domain Layer
- Service Layer
- Client Layer
- Transport Layer
Business layer doesn't really belong to the library itself, it is application specific. This layer contains business logic, which operates in business terms. For example, your use case requires you to create a Google Analystics visitor statistics widget for your backend panel. It is important to note, that use case does not define which protocol do you need to use or which request you need to make. All because it's irrelevant, the use case is still valid regardless of technical details. We can use some pseudocode to describe usage of the client library in the use case:
gaService.getVisitorStatistics(). Actually, the real code shouldn't look much different. As we see, business layer makes use of a service class, which belongs to a service layer.
Service layer is also application specific and is not shipped with a library. This layer can translate business requirement into Client terms. In the previous layer we have stated, that we need visitor statistics. This layer knows how to instantiate a Client and which request to invoke to get the data. Also here you get the data from the response object and prepare it to pass back to business layer. This layer can be implemented as an Adapter pattern, which adapts Client Library to the required interface.
Now we have crossed application boundry. This is no longer a part of the application, but a distributable and reusable library by itself. Client knows how send requests. It has a public method
request(RequestInterface); and that is basically it. In this layer we define Request and Response classes, which contain the that data you pass to the API and receive from the it. If it's a SOAP client, Request can produce XML (or an array, because PHP's SoapClient in WSDL mode is smart enough) or a JSON string. Response must be able to parse response SOAP XML or JSON and provide you all the methods you need to retrieve data. It can be argued, that I'm missing a presenter layer here, which is true. From my experience, if a format changes most of the request structure change as well. So while these concerns are separate, in practice they usually change together.
This is your HTTP client, SOAP client or any other client, that doesn't care about contents of your request. It's just a transmission tool, a separate library which hides behind an interface (e.g. PSR-18).
File structure of the client looks like this.
This structure helps me to create clients blazingly fast.
- It's easy to test. You can isolate every layer and mock the adjacent layers.
- Every layer concentrates on a specific concern. If changes are required, they usually involve just one layer.
- Extending is easy. To create new requests you just need to add new Request classes, no changes to client needed.