Joe's
Digital Garden

Application Service

The Interface

interface IApplicationService
{
    /**
     * Execute the service and return any results generated
     * @return array
     */
    public function execute(array $args): array
}

The Application Service contains a huge chunk of the more complex business logic in which various entities are integrated together to perform some kind of action against the domain model. This is also the apropriate layer for validating that request is even possible with the current state of the model.

In practice, the Application Service is an instance of the Command pattern -- a closure in the form of a class. It's constructor wraps several utilities and only a single public method exists to accept the request array (previously normalized by a [[Validator]] instance).

I am still playing with the ideal form of the Application Service. PHP now supports first class functions, so closures are now possible. Another alternative is the Builder pattern, that is a chain of setters instead of a large constructor can be useful for "building" a complex command that is easily self documenting.

The structure of the execution function itself is also debatable. The current method of passing an array and receiving an array tells us very little about the contents of each. Possible solutions include

  1. Continue to pass an array knowing these downsides exist.
  2. Pass the Validator itself into the method. However, this creates a dependency between the service and the validator.
  3. Deconstruct the array into a series of parameters that are passed. This could result in a very unweidly function signature.
  4. Use the Builder pattern as mentioned above, however there is no way to ensure that all required building methods were called before execution
  5. Define a type that a service can accept and return that wraps the array and response values. This could balloon the number of types in the system though.
  6. Use the new anonymous classes to do #5, but avoid concretely defining all the types the services accept and return.

Concrete Examples

class UpdateCustomerService implements IApplicationService
{
    const CUSTOMER_MISSING = "Customer could not be found.";

    private $CustomerRepository;

    public function __construct(CustomerRepository $CustomerRepository)
    {
        $this->CustomerRepository;
    }

    public function execute(array $args): array
    {
        $Customer = $this->CustomerRepository->fetchCustomer($args['id']);

        if (!$Customer) {
            return [new \RuntimeException(self::CUSTOMER_MISSING), null];
        }

        $Customer = $Customer->from([
            'firstName' => $args['firstName'] ?? null,
            'lastName'  => $args['lastName'] ?? null
        ]);

        return [null, $Customer];
    }
}

$CustomerRepository = new $CustomerRepository($pdo);
list ($error, $Customer) = ((new UpdateCustomerService($CustomerRepository)))
    ->execute($request);

if ($error) {
    throw $error;
}

$CustomerRepository->saveCustomer($Customer);

This is the common formulation I use for services as a Command. The constructor accepts all objects that are required to perform it's execution, commonly these will be [[Repository]] objects. In a complex service we might have multiple repositories for retrieving many entities from the database or serialized over REST APIs -- the logic could become very complex needing additional strategies (passed in through the constructor) or calling many methods on these entities to compute the final result. Similarly, a read operations might retrieve many objects that the controller needs to pass on to the view.

The Application Service executes on an request (not shown, this request is retrieved from a [[Validator]] object). Here we validate that the request is possible against the model itself, that is, does the necessary entities exist in our system (in this case a pre-existing Customer entity) and are they in a state that can accept the request itself (necessary if the state change requires some predicate). The Customer state is then updated per the request and returned.

Notice the structure of the return array. I am borrowing this from Go's error handling. Instead of returning a simple Customer object, we return an array in which the first value is either null or an exception. The second and subsequent properties are then any other objects retrieved from the repositories. Back in the controller, we evaluate and handle the error if it exists (in this case we throw it), and persist any objects that need persisting.

Not shown, the controller will then construct a response and return it.

class UpdateCustomerService implements IApplicationService
{
    const CUSTOMER_MISSING = "Customer could not be found.";

    private $CustomerRepository;

    private $firstName;
    private $id;
    private $lastName;

    public function __construct(CustomerRepository $CustomerRepository)
    {
        $this->CustomerRepository;
    }

    public function execute(): array
    {
        $Customer = $this->CustomerRepository->fetchCustomer($this->id);

        if (!$Customer) {
            return [new \RuntimeException(self::CUSTOMER_MISSING), null];
        }

        $Customer = $Customer->from([
            'firstName' => $this->firstName ?? null,
            'lastName'  => $this->lastName ?? null
        ]);

        return [null, $Customer];
    }

    public function updateCustomerOfId(int $id): UpdateCustomerService
    {
        $this->id = $id;
        return $this;
    }

    public function withFirstName(string $firstName): UpdateCustomerService
    {
        $this->firstName = $firstName;
        return $this;
    }

    public function withLastName(string $lastName): UpdateCustomerService
    {
        $this->lastName = $lastName;
        return $this;
    }
}

$CustomerRepository = new $CustomerRepository($pdo);
list ($error, $Customer) = (new UpdateCustomerService($CustomerRepository))
    ->updateCustomerOfId($request['id'])
    ->withFirstName($request['firstName'])
    ->withLastName($request['lastName'])
    ->execute();

if ($error) {
    throw $error;
}

$CustomerRepository->saveCustomer($Customer);

The same as before, but in this case we are using chained building methods to create a more verbose construction of the service that is self documenting about the behavior that this service will execute against the Customer. This is useful in instances where the structure of the request could signfigantly change the decisions that the Service will make in the domain and we want to make this clear to the caller.

Last, the service could be implemented as a closure:

$CustomerRepository = new CustomerRepository($pdo);

$UpdateCustomerService = function(array $args) use ($CustomerRepository) {
    $Customer = $CustomerRepository->fetchCustomer($args['id']);

    if (!$Customer) {
        return [new \RuntimeException(self::CUSTOMER_MISSING), null];
    }

    $Customer = $Customer->from([
        'firstName' => $args['firstName'] ?? null,
        'lastName'  => $args['lastName'] ?? null
    ]);

    return [null, $Customer];
};

list ($error, $Customer) = $UpdateCustomerService($args);

if ($error) {
    throw $error;
}

$CustomerRepository->saveCustomer($Customer);

This solution, which I am still playing with, is much more compact that the Class-based solution. The original class was 26 lines (46 for the builder solution), the closure is 13. In any case the controller needs four statements to execute the command. Does the reduction in lines outweigh the loss of clarity that the longer solutions provide?

Linked References

  • layered-web-application-architecture
    • Controller delegates to an [[Application Service]] to perform the Command and marshalls any resources the service requires.
  • php-models

    From the storage medium, the model may be hydrated in part or in full. It may be read in immutably or muttably. In general though, I disagree with the "fat" vs "anemic" models concept. The model is best served as a kind of struct as we see in newer languages. It should principly exist to contain and represent it's properties. Methods of mutability or interactions with other Entities are best handled in a [[Application Service]] layer.

  • php-view

    In general, I see the direction of PHP development heading towards expressing the domain in the form of a REST API that returns responses in the JSON-API format. This back-end feeds into a front-end expressed in HTML and Javascript which manages the complex interactions of UX state. This makes the "view" layer of the application extremely simple -- the view is just the JSON serialization of the entities returned by the [[Application Service]] layer.

  • php-view

    The View object is created by the Controller and then fed the various Entities returned from the [[Application Service]] layer. This ensures that all properties required by the View are either set or set to some known default. It also allows us to perform any last minute formatting of the data such as [[datetime handling]], normalizing number or currency representations (trailing zeros, comma vs periods, currency signs), etc. We should avoid any kind of business logic in the View model. Simple formatting is one thing, but no where should we declare new symbols nor modify existing symbols except changing their expression -- e.g. converting $20.00 to $20,00 does not change the value, but the expression 20.00 + 20.00 does.