It can be too broad a topic to discuss application services without a specific context. So to that end I am clarifying that the context is an n-tier design utilizing object-oriented practices for supporting the modeling of a common business application.
What are application services and how do they fit into an Object-Oriented N-Tier solution?
A middle tier in many business applications is the business tier. This tier may consist of multiple layers of code including but not limited to a business objects layer, data translation objects layer, and in many cases a service objects layer. This layer of service objects is often called Application Services because it can be thought of as the essence of the application. I like to think of the service layer's interface as the application's interface. The business objects might be the brains of the operation, but the glue that holds the application together and clearly defines what the application can do is the application services layer. The presentation layer code can access the application services and a limited set of business objects and their interfaces in order to get work done.
An application service’s public API abstracts a business process with known inputs, outputs and steps as defined by a business analyst or user. In fact, the code in a well-defined business service should be readable by a business analyst. The result of creating a service class in front of all the object manipulation, decisions, and system state changes going on during a process is a higher level of abstraction. This pattern is essentially a façade pattern as documented in the quintessential book Design Patterns.
- Separation of concerns (yes, my number one answer for everything)
- Application Services can provide a façade to complex object models or common tasks involving multiple objects.
- When requirements unrelated to the core business like security or logging is needed we can implement this code outside of the business objects yet guarantee the code is always invoked.
- Encapsulate code that changes the state of the system and may require transactions while allowing business object methods to be side-effect-free and easily testable.
- Help model a process as defined by business analysts
- Complex logic that is often viewed as a flowchart or procedural-logic-tree by business analysts can be written in a service. A business analysis could even step through the code and understand it if written well (using the same terms and phraseology of the business analysts). The brains of the operation stays in the business objects, but the service can manage the process.
- Help our object model evolve in an iterative fashion
- Object models evolve over time as requirements get more stable, changes become necessary, development paradigms shift, and we learn more about the application we are building. Sometimes, just getting the code written in a black box manner (W and X go in, Y and Z come out) can help us see our needed object model as we refactor the code.
My rule of thumb is that if the call changes the state of the system then it should be in the service. There are many benefits to side-effect-free functions and knowing that every business object’s method is side-effect-free it very useful (see also: CQS: command-query-segregation principle). That said, a service that does not change the system, but rather calculates a result or determines a collection of result objects might not have a clear place to live. In this case, my first vote would be to put the method in a corresponding object, an object that appears at the top of the hierarchy. OO heuristics would state that the method goes with the object that contains the most data involved in the logic. However, if this object is not very obvious then this is just another benefit of services. Instead of the client looking through the interfaces to all the related objects, hopefully they take a quick peek at the related application services.
· State is never really needed unless the service provides sequential calls that rely on data produced in prior calls. Even then, the client could hold that data and spare the service this concern.
However, it can be very helpful for client code if the service class maintained state from call to call. This kind of a service might be called a Manager Service since besides just providing simple services, it helps manage the operation. For example a Quiz Manager might have methods for stepping back and forth through Questions and allowing the client to submit and change answers. The manager service would keep track of what question the client is on and whether or not the test is complete etc.
· Without state the service can be used without modification for both stateless and state-full clients. For example a web front could use the service in the same way a Windows desktop application would.
· Without state being shared between multiple calls in a service, each individual call can be tested separately from the others.
This is a valid point, however hopefully the testing of the service is just a test of the process flow. The actual brains of the operation that will get the most testing will be the methods on the business objects that the service talks to.
· If you give a service state it can mean that less objects need to be exposed to the client. For example, if step 1 on a stateless service creates object X then you have to return object X to the client, even if the only purpose for X is to pass it in to service method step 2. If the service had state, object X might never be exposed and the step 2 would just check for existence before doing its thing.
· A class without state could also provide its methods at static (shared in VB.NET) so a call to create an instance is unnecessary
The service layer should be in the same tier as the business object layer so we can take better advantage of object and object property scope. With a well-designed service layer façade in the same tier as the business objects many business objects do not have to be public and ones that are public can keep many of their attributes and methods internal at worst.
With all services in the same tier as the business objects, it is easy to see what processes are affected by inevitable changes to a business object’s interface by a simple compilation of one project (which again is just a benefit of minimal scope)
From the dawn of object-oriented development there has been an emphasis on using coding constructs to represent real things in order to provide a clear mapping from business terms and their counterparts in code. Possibly even more important than matching up business object names with real business entities is matching up business processes and scenarios with services that match the lingo. When a business analyst explains the steps to determine the result of an algorithm or designs a flow chart with decision trees, it pays off greatly to match those exact steps, flows, and decisions in code. If the objects are named after real life entities, and the methods named after real life questions then you will find that the code in a service reads just like the documentation of the process, and it should. On many occasions I have had a business expert looking over my shoulder reading the code that steps through a process with no confusion on their part in regards to programming syntax.
We should keep in mind though that the business rules to be applied and decisions to be made are all done by business objects. The application service just knows the steps, the details are in the business objects.
Nice: If myPerson.CanVote then
Not so nice: If myPerson.Age >= 18 then
What I mean by this is that we need a place to put all the code that has to do with the fact that our business is now represented in software, but has little or nothing to do with the actual business. For example, if I want to transfer money between two accounts in a banking application I would probably use two Account objects and a Dollar Amount object. However, because this is an application, when someone requests to transfer money I might have to verify security and write to an event log that tracks things people do with the system, etc. I want to make sure that this always happens when a transfer is completed. So instead of making sure the phone system, web site, and Windows application all complete these same steps I make sure that the only way to complete a transfer is to use the service I provide. The business objects know nothing about logging and security, it’s not their concern, but the service makes sure that these things are taken care of.
Factories are a service, granted, a specialized service that is more cohesive in regards to the objects is serves up. Factories create objects. They might be used for a simple separation of concerns so a class is not concerned with how its objects are built, or more complex reasons like data translation and abstract object creation. However it can be unclear where the logic exists for determining what object or objects a client should get on request. For example, if client code needs to get Documents related to an Account Owner’s Brokerage Account, it would be natural for the client to ask a DocumentFactory. However, what if determining the Documents an Account Owner can see is rather complex and requires information not coupled to documents like security concerns, or we want to log every time someone requests documents? In an effort to keep the Document Factory cohesive and uncoupled to security and logging concerns we might consider a service. The possible downside is that client code does not know if it should look to the Documents Service or Factory to get the object. However, if the factory does not have a public method for getting documents, a service would be the next logical place to look.
I have read, heard and engaged in discussions about how application services amount to a procedural design: the antithesis of object-oriented design. I will admit to some truthfulness in that statement, but would suggest it is too broad. Rather, services that act as god classes are not object-oriented, but well-designed classes that perform a service constitute a solid design. In the seminal book Object-Oriented Design Heuristics, the object HeatFlowRegulator is an example of a Service class done right.