Domain Services

This guide documents Apache Isis' domain services, both those that act as an API (implemented by the framework for your domain objects to call), and those domain services that act as an SPI (implemented by your domain application and which are called by the framework).

Types of Domain Service

The domain services can be categorised as API or SPI, and within a particular architectural layer:

categories

What distinguishes API from SPI is primarily whether it is called by the domain application itself, or is called by the framework:

  • All of the API domain services provide a default implementation, and so can always be called by domain objects.

  • Most of the SPI domain services also have a default implementation, although in many cases this is a fallback implementation.

In one sense all API domain services are also SPI, in that they can be overridden by the domain programmer providing another domain service implementing the same type. For example, the framework-provided implementation of RepositoryService could be overridden this way, perhaps for more monitoring or caching purposes. This is done using the @javax.annotation.Priority annotation.

Generally speaking in such cases the earliest encountered implementation (= highest priority) will be used instead of the framework-provided implementation. For some services, though, all available implementations are delegated to; these are typically subscribers such as EntityPropertyChangeSubscriber.

A small number of domain services can be considered both API and SPI; a good example is the EmailService that is of direct use for domain objects wishing to send out emails, but is also used by the framework to support the user registration functionality supported by the Web UI (Wicket viewer). The same is true of the EventBusService; this can be used by domain objects to broadcast arbitrary events, but is also used by the framework to automatically emit events for @Action#domainEvent() etc. For these hybrid services we have categorized the service as an API service.

This body of this guide has one page per domain service, alphabetically. To help you learn about them, the tables below also group the services by category.

Presentation Layer API

Domain service APIs for the presentation layer allow the domain objects to control aspects of the user interface.

Table 1. Presentation Layer API
API Description

AcceptHeaderService

Request-scoped access to HTTP Accept headers.

BookmarkUiService

Manage bookmarks and breadcrumbs.

DeepLinkService

Obtain a URL to a domain object (eg for use within an email or report)

The implementations are specific to the particular viewer (Web UI (Wicket viewer) or REST API (Restful Objects viewer)) so domain code must guard against them being unavailable in some cases.

Presentation Layer SPI

The persistence layer SPIs influence how the framework persists domain objects, for example controlling how to create an audit log of changes to domain objects.

Table 2. Presentation Layer SPI
SPI Description

ContentMappingService

(Attempt to) map the returned data into the representation required by the client’s HTTP Accept header.

The framework will call all available implementations until a mapping is made (chain of responsibility pattern).

EmailNotificationService

Notify a user during self-registration of users.

ErrorReportingService

Record details of an error occurring in the system (eg in an external incident recording system such as JIRA), and return a more friendly (jargon-free) message to display to the end user, with optional reference (eg XXX-1234).

ExceptionRecognizerService

Convert certain exceptions (eg foreign or unique key violation in the database) into a format that can be rendered to the end-user.

HintIdProvider

Provide a "logical" identity for view models such that UI hints can be stored for them.

HintStore

Stores UI hints on a per-object basis. For example, the viewer remembers which tabs are selected, and for collections which view is selected (eg table or hidden), which page of a table to render, or whether "show all" (rows) is toggled.

LocaleProvider

Request-scoped service to return the locale of the current user, in support of i18n (ie so that the app’s UI, messages and exceptions can be translated to the required locale by the TranslationService.

RoutingService

Return an alternative object than that returned by an action.

TableColumnOrderService

Allows the columns of a parented or standalone table to be reordered, based upon the parent object, collection id and type of object in the collection.

TableColumnVisibilityService

Filters the columns of a parented or standalone tables.

SecMan provides an implementation of this service that filters out columns based on security permissions.

TranslationService

Translate an app’s UI, messages and exceptions for the current user (as per the locale provided by LocalProvider.

UrlEncodingService

Converts strings into a form safe for use within a URL. Used to convert view models mementos into usable URL form.

UserProfileUiModelProvider

Obtain an alternative (usually enriched/customized) name for the current user, to render in the UI.

Application Layer API

Domain service APIs for the application layer allow the domain objects to control aspects of the application layer, such as sending info messages back to the end-user.

Table 3. Application Layer API
API Description

CommandExecutorService

Executes the specified Command.

ImpersonateMenu

Provides a UI to allow the current user to be impersonated as some other user.

The actions in this menu are restricted to non-production use only.

InteractionContext

Request-scoped access to the current member execution (action invocation or property edit), represented as the Interaction context.

MessageService

Methods to inform or warn the user, or to raise errors.

SudoService

Typically for use in testing while running fixture scripts, allows a block of code to run as a specified user account.

TitleService

Methods to programmatically obtain the title or icon of a domain object.

TransactionService

Methods for managing transactions.

WrapperFactory

Interact with another domain object "as if" through the UI (enforcing business rules, firing domain events). It can also be used to dispatch actions asynchronously, to be run through a java.util.concurrent.ExecutorService.

Application Layer SPI

Domain service SPIs influence how the framework handles application layer concerns, for example which home page to render to the end-user.

Table 4. Application Layer SPI
API Description

AuthenticationConverter

SPI service for Spring Security to convert a Spring Authentication into an Apache Isis UserMemento.

Several implementations are provided by default for most of the common representations of a user principal. This SPI does though provide additional flexibility for other security technologies that may be supported by Spring.

CommandSubscriber

SPI service to listen on command instances, ie representations of an action invocation or property edit. Used for command/auditing and background services.

ExecutionSubscriber

SPI service to listen on command instances, ie representations of an action invocation or property edit. Used for command/auditing and background services.

HealthCheckService

Performs a health check so that the runtime infrastructure can determine if the application is still healthy (and perform remedial action, such as restarting the app, if not).

HomePageResolverService

Returns the home page object, if any is defined.

ImpersonateMenuAdvisor

Provides a mechanism to influence the UI provided by ImpersonateMenu

ImpersonatedUserHolder

Provides a mechanism to hold the impersonated user

Core/Domain API

The core/domain APIs provide general-purpose services to the domain objects, for example obtaining the current time or user, or instantiating domain objects.

Table 5. Core/Domain Layer API
API Description

ClockService

Access the current time (and for testing, allow the time to be changed)

ConfigurationViewService & ConfigurationMenu

Backing service which creates the list of configuration properties for ConfigurationMenu.

EventBusService

Programmatically post events to the internal event bus. Also used by Apache Isis itself to broadcast domain, UI and lifecycle events.

FactoryService

Methods to instantiate and initialize domain objects

Scratchpad

Request-scoped service for interchanging information between and aggregating over multiple method calls.

ServiceInjector

Programmatically inject services into arbitrary objects.

ServiceRegistry

Registry of all domain services, for service locator pattern.

UserService

Methods to access the currently-logged on user.

Integration API

The integration APIs provide functionality to the domain objects to integrate with other bounded contexts, for example sending an email or serializing an object out to XML.

see also the Bounded Context Mappings catalogue.
Table 6. Integration API
API Description

BookmarkService

Convert object reference to a serializable "bookmark", and vice versa.

EmailService

Send a HTML email, optionally with attachments.

JaxbService

Marshal and unmarshal JAXB-annotated view models to/from XML.

XmlSnapshotService

Generate an XML representation of an object and optionally a graph of related objects.

Integration SPI

The integration SPIs allow the framework to automate the exchange of data between bounded contexts automatically.

see also the Bounded Context Mappings catalogue.
Table 7. Integration SPI
API Description

CommandDtoProcessorService

SPI to support representation of commands as XML over REST, in particular to support master/slave replay of commands.

CommandExecutorService

Service used to execute commands. One use case is to replay commands from a primary onto a secondary (see Command Replay ; another is in support of async commands (using WrapperFactory ).

CommandSubscriber

SPI to allow commands to be processed on completion. The Command Log extension implements the SPI in order to persists commands for audit or replay.

EntityChangesSubscriber

Summarises changes made to entities within an interaction.

EntityPropertyChangeSubscriber

Publish the changes to all changed properties of all domain object within an interaction.

ExecutionSubscriber

Publish any action invocations/property edits and changed objects, typically for interchange with an external system in a different bounded context.

Metadata API

The metadata APIs provide access to the framework’s internal metamodel. These are generally of use to support development-time activities, for example creating custom UIs through Swagger.

Table 8. Metadata API
API Description

ApplicationFeatureRepository

Provides access to string representations of the features (package, class, class members) of the domain classes within the metamodel.

LayoutService & LayoutServiceMenu

Use to obtain the resolved Xxx.layout.xml for a class, and the top-level menubars.layout.xml files.

MetaModelService & MetaModelServiceMenu

Access to certain information from the Apache Isis metamodel.

ServiceRegistry

Methods to access and use other domain services.

SwaggerService

Exports Swagger spec files, eg to generate client-side stubs for use in a custom REST client.

Metadata SPI

The metadata SPIs allow the framework to obtain metadata from alternative locations.

Table 9. Metadata SPIs
API Description

GridLoaderService

Responsible for loading a grid layout for a domain class, eg from a layout.xml file.

GridService

A facade on top of both GridLoaderService and GridSystemService, thus being able to return normalized grids for any domain class.

GridSystemService

Validates and normalizes the grid layout for a domain class (with respect to a particular grid system such as Bootstrap3), also providing a default grid (for those domain classes where there is no grid layout).

MenuBarsLoaderService

Low-level SPI to load the serialized form of the menubars (normally: the menubars.layout.xml) into memory. Used by

MenuBarsService

Constructs and normalizes the in-memory representation of the menubars.

TranslationsResolver

Obtain translations for a particuar phrase and locale, in support of i18n (ie so that the app’s UI, messages and exceptions can be translated to the required locale by the TranslationService

Persistence Layer API

The persistence layer APIs provide domain objects with tools to manage the interactions with the persistence layer, for example adding on-the-fly caching to queries that are called many times within a loop.

Table 10. Persistence Layer API
API Description

MetricsService

Gathers and provides metrics on the numbers of objects used within a transaction.

QueryResultsCache

Request-scoped caching of the results of queries (or any data set generated by a given set of input arguments).

RepositoryService

Methods to help implement repositories: query for existing objects, persist new or delete existing objects

TransactionalProcessor

Running commands within a transaction

TransactionService

Transaction management

Persistence Layer SPI

The persistence layer SPIs are to control persistence and caching of domain objects.

Table 11. Persistence Layer SPI
API Description

QueryResultsCacheControl

Controls whether the QueryResultsCache is enabled.

Security SPI

The security SPIs influence how the framework handles various security concerns.

see also the Security Guide.
Table 12. Security SPI
API Description

EntityPropertyChangesSubscriber

Create an audit record for every changed property of every changed object within a transaction.

SessionLoggingService

Records each login and logout by end-users.

UserRegistrationService

Create a new user account with the configured security mechanism.

Public API vs Internal Services

The vast majority of Apache Isis' domain services are defined in Apache Isis' applib (o.a.i.core:isis-applib module) as stable, public classes. Importantly, this also minimizes the coupling between your code and Apache Isis, allowing you to easily mock out these services in your unit tests.

The framework also defines a number of "internal" services. These are not part of the framework’s formal API, in that they use classes that are outside of the applib. These internal framework services should be thought of as part of the internal design of the framework, and are liable to change from release to release.

Using the services

Apache Isis includes an extensive number of domain services for your domain objects to use; simply define the service as an annotated field and Apache Isis will inject the service into your object.

For example:

public class Customer {

    public void sendEmail( String subject, String body) {
        List<String> cc = Collections.emptyList;
        List<String> bcc = Collections.emptyList;
        emailService.send(getEmailAddress(), cc, bcc, subject, body);
    }
    public boolean hideSendEmail() {
        return !emailService.isConfigured();
    }

    @Inject                                                            (1)
    EmailService emailService;
}
1 Service automatically injected by the framework.

You may also need to @Import the module that contains the service into your application’s AppManifest (though all of the services in core will be available automatically).

For objects that are already persisted, the service is automatically injected just after the object is rehydrated by JDO/DataNucleus.

For transient objects (instantiated programmatically), the FactoryService#viewModel(…​) or the RepositoryService#detachedEntity(…​)'s will automatically inject the services.

Alternatively the object can be instantiated simply using new, then services injected using ServiceInjector's injectServicesInto(…​) method.

Overriding the services

The framework provides default implementations for many of the domain services. This is convenient, but sometimes you will want to replace the default implementation with your own service implementation. This is most commonly done using the @javax.annotation.Priority annotation. The PriorityPrecedence class provides some pre-defined precedences.

  • If a scalar field is being injected to, the earliest implementation encountered (= highest priority) is used

  • If a list is being injected to, then all available implementations are injected, again with the beans with earlier precedences sorting before those with later precedences.

For some domain services, all framework will delegate to all available implementation (ie consumes a list of implementations), and uses a chain-of-responsibility pattern): These include:

For example, suppose you wanted to provide your own implementation of LocaleProvider. The default implementation has these annotations:

@Service
@Named("isisWicketViewer.LocaleProviderWicket")
@Priority(PriorityPrecedence.MIDPOINT)
@Qualifier("Wicket")
@Log4j2
public class LocaleProviderWicket implements LocaleProvider { /* ... */ }

To override this, use something like:

@Service
@Priority(PriorityPrecedence.EARLY)
public class MyLocaleProvider implements LocaleProvider { /* ... */ }

It’s not necessary to annotate @Named or @Qualifier, but could be considered good practice.

Command and Events

A good number of the domain services manage the execution of action invocations/property edits, along with the state of domain objects that are modified as a result of these. These services capture information which can then be used for various purposes, most notably for auditing or for publishing events, or for deferring execution such that the execution be performed in the background at some later date.

The diagram below shows how these services fit together. The outline boxes are services while the coloured boxes represent data structures - defined in the applib and therefore accessible to domain applications - which hold various information about the executions.

commands and events

To explain:

  • the (request-scoped) InteractionContext domain service acts as a factory for the Interaction object, which keeps track of the call-graph of executions (Interaction.Execution) of either action invocations or property edits.

    In the majority of cases there is likely to be just a single top-level node of this graph, but for applications that use the WrapperFactory extensively each successive call results in a new child execution.

  • the Interaction also holds a reference to the Command, which represents the top-level intention to invoke the action / edit the property.

  • before and after each action invocation/property edit, a domain event is may be broadcast to all subscribers. Whether this occurs depends on whether the action/property has been annotated (using @Action#domainEvent() or @Property#domainEvent()).

    (Note that subscribers will also receive events for vetoing the action/property; this is not shown on the diagram).

  • As each execution progresses, and objects that are modified are "enlisted" (managed by an service internal to the framework). Metrics as to which objects are merely loaded into memory are also captured using the MetricsService (not shown on the diagram).

  • At the end of each execution, details of that execution are published through the (internal) ExecutionPublisher domain service. This is only done for actions/properties annotated appropriate (with @Action#executionPublishing() or @Property#executionPublishing()).

    The internal service delegates in turn to any registered ExecutionSubscribers (there may be more than one).

  • At the end of each transaction, details of all changed objects are published to any registered EntityChangesSubscriber implementations and (through an internal service) also to any registered EntityPropertyChangeSubscriber implementations. Only domain objects specified to be published with @DomainObject#entityChangePublishing() are published.

    Note that it’s possible for there to be more than one transaction per top-level interaction, by virtue of the TransactionService.

  • At the end of the entire interaction [1], details of the top-level Command are sent to each CommandSubscriber. This captures whether the command succeeded or failed.

    The Command Log extension uses this to persist a log of commands, for auditing or to support regression testing with the Command Replay extension.

Implementations of CommandSubscriber can use the Command#getMemento() method to obtain a XML equivalent of that Command, reified using the cmd.xsd schema. This can be converted back into a CommandDto using the CommandDtoUtils utility class (part of the applib).

Similarly, implementations of ExecutionSubscriber can use the InteractionDtoUtils utility class to obtain a InteractionDto representing the interaction, either just for a single execution or for the entire call-graph. This can be converted into XML in a similar fashion.

Likewise, the PublishedObjects class passed to the PublisherService at the end of the interaction provides the PublishedObjects#getDto() method which returns a ChangesDto instance. This can be converted into XML using the ChangesDtoUtils utility class.

One final point: multiple ExecutionSubscriber implementations are supported because different implementations may have different responsibilities.

However, the SPI can also be used for profiling; each execution within the call-graph contains metrics of the number of objects loaded or modified as a result of that execution, and thus could be used for application profiling. The framework provides a default PublisherServiceLogging implementation that logs this using LOG4J2.


1. although rare, there can be multiple transactions in a single interaction