The service layer is responsible for exposing functionality made available by the logical layer to external consumers over a network via technical protocols.
Before you start creating your services you should consider some general design aspects:
-
Do you want to create a RPC service?
-
Or is your problem better addressed by messaging or eventing?
-
Who will consume your service?
-
Do you have one or multiple consumers?
-
Do web-browsers have to use your service?
-
Will apps from other vendors or parties have to consume your service that you can not influence if the service may have to change or be extended?
-
For RPC services consumed by other applications we use versioning to prevent incompatibilities between applications when deploying updates. This is done by the following conventions:
-
We define a version number and prefix it with
v
(e.g.v1
). -
If we support previous versions we use that version numbers as part of the Java package defining the service API (e.g.
com.foo.application.component.service.api.v1
) -
We use the version number as part of the service name in the remote URL (e.g.
https://application.foo.com/services/rest/component/v1/resource
) -
Whenever breaking changes are made to the API, create a separate version of the service and increment the version (e.g.
v1
→v2
) . The implementations of the different versions of the service contain compatibility code and delegate to the same unversioned use-case of the logic layer whenever possible. -
For maintenance and simplicity, avoid keeping more than one previous version.
For services that are consumed by clients with different technology, interoperability is required. This is addressed by selecting the right protocol, following protocol-specific best practices and following our considerations especially simplicity.
The term service is quite generic and therefore easily misunderstood. It is a unit exposing coherent functionality via a well-defined interface over a network. For the design of a service, we consider the following aspects:
-
self-contained
The entire API of the service shall be self-contained and have no dependencies on other parts of the application (other services, implementations, etc.). -
idempotence
E.g. creation of the same master-data entity has no effect (no error) -
loosely coupled
Service consumers have minimum knowledge and dependencies on the service provider. -
normalized
Complete, no redundancy, minimal -
coarse-grained
Service provides rather large operations (save entire entity or set of entities rather than individual attributes) -
atomic
Process individual entities (for processing large sets of data, use a batch instead of a service) -
simplicity
Avoid polymorphism, RPC methods with unique name per signature and no overloading, avoid attachments (consider separate download service), etc.
Your services are the major entry point to your application. Hence, security considerations are important here.
See REST Security.