Domain Services
This guide documents Apache Causeway' 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:
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.
API | Description |
---|---|
Request-scoped access to HTTP Accept headers. |
|
Manage bookmarks and breadcrumbs. |
|
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.
SPI | Description |
---|---|
(Attempt to) map the returned data into the representation required by the client’s HTTP The framework will call all available implementations until a mapping is made (chain of responsibility pattern). The framework itself provides a couple of implementations: |
|
Notify a user during self-registration of users. |
|
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 |
|
Convert certain exceptions (eg foreign or unique key violation in the database) into a format that can be rendered to the end-user. All available implementations are called (chain of responsibility pattern). |
|
Provide a "logical" identity for view models such that UI hints can be stored for them. |
|
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. |
|
Return an alternative object than that returned by an action. |
|
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. |
|
Filters the columns of a parented or standalone tables. SecMan provides an implementation of this service that filters out columns based on security permissions. |
|
Translate an app’s UI, messages and exceptions for the current user (as per the language provided by LanguageProvider. |
|
Converts strings into a form safe for use within a URL. Used to convert view models mementos into usable URL form. |
|
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.
API | Description |
---|---|
Service used to execute a specified Command. One use case is in support of async commands (using WrapperFactory ). |
|
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. |
|
Request-scoped access to the current member execution (action invocation or property edit), represented as the Interaction context. |
|
Access to the current InteractionLayer, where the lowest layer is in effect a short-lived session connected to the database, with a transaction started automatically. |
|
Extends InteractionLayerTracker, providing additional "session" management capabilities. In particular, can be used to open new InteractionLayer layers (= "session"), similar to an "su" command in Unix, with the user or clock etc. temporarily altered. Can also be used to close all layers (= "close the session"), which commits the transaction. More precisely: provides access to the current Interaction / InteractionLayer, with the ability to |
|
Methods to inform or warn the user, or to raise errors. |
|
Typically for use in testing while running fixture scripts, allows a block of code to run as a specified user account. |
|
Methods to programmatically obtain the title or icon of a domain object. |
|
Provides the ability to submit work within a transaction, specifying whether that transaction should be nested or new (suspending the current). |
|
Extends TransactionalProcessor to provide the ability to query the current transaction state and to flush the current transaction. |
|
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 |
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.
API | Description |
---|---|
SPI service for Spring Security to convert a Spring Authentication into an Apache Causeway 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. |
|
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). |
|
Returns the home page object, if any is defined. |
|
Provides a mechanism to influence the UI provided by ImpersonateMenu |
|
SPI service to listen on users logging in and logging off their (long-lived) session. The SessionLogger is a trivial implementation that just logs the session start/end times. |
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.
API | Description |
---|---|
Access the current time (and for testing, allow the time to be changed) |
|
Backing service which creates the list of configuration properties for ConfigurationMenu. |
|
Programmatically post events to the internal event bus. Also used by Apache Causeway itself to broadcast domain, UI and lifecycle events. |
|
Methods to instantiate and initialize domain objects |
|
Obtain the preferred language of the current user. |
|
Provides a list of the available locales. Used by the framework itself, eg to prompt the list of locales when a user logs in through the Wicket viewer. |
|
Request-scoped service for interchanging information between and aggregating over multiple method calls. |
|
Programmatically inject services into arbitrary objects. |
|
Registry of all domain services, for service locator pattern. |
|
|
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.
API | Description |
---|---|
Convert object reference to a serializable "bookmark", and vice versa. |
|
Send a HTML email, optionally with attachments. |
|
Marshal and unmarshal JAXB-annotated view models to/from XML. |
|
Tools for working with W3C XML Documents. |
|
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.
API | Description |
---|---|
SPI to support representation of commands as XML over REST, in particular to support master/slave replay of commands. |
|
SPI service to listen on command instances, ie representations of an action invocation or property edit. Used for command/auditing and background services. The framework provides a couple of implementations:
|
|
Summarises changes made to entities within an interaction. The EntityChangesLogger is a trivial implementation that just logs the commands. |
|
Publish the changes to all changed properties of all domain object within an interaction. The EntityPropertyChangeLogger is a trivial implementation that just logs the commands. |
|
SPI service to listen on command executions, ie representations of an action invocation or property edit. The framework provides several implementations:
|
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.
API | Description |
---|---|
Provides access to string representations of the features (package, class, class members) of the domain classes within the metamodel. |
|
Use to obtain the resolved |
|
Access to certain information from the Apache Causeway metamodel. |
|
Access to a downloadable site map of the application’s features, as an AsciiDoc document. The intention is to use this as the basis for a user guide or to develop a manual regression test plan. |
|
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.
API | Description |
---|---|
Responsible for loading a grid layout for a domain class, eg from a |
|
A facade on top of both GridLoaderService and GridSystemService, thus being able to return normalized grids for any domain class. |
|
Validates and normalizes the grid layout for a domain class (with respect to a particular grid system such as Bootstrap), also providing a default grid (for those domain classes where there is no grid layout). |
|
Low-level SPI to load the serialized form of the menubars (normally: the |
|
Constructs and normalizes the in-memory representation of the menubars. |
|
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.
API | Description |
---|---|
Gathers and provides metrics on the numbers of objects used within a transaction. |
|
Request-scoped caching of the results of queries (or any data set generated by a given set of input arguments). |
|
Methods to help implement repositories: query for existing objects, persist new or delete existing objects |
|
Running commands within a transaction |
|
Transaction management |
Persistence Layer SPI
The persistence layer SPIs are to control persistence and caching of domain objects.
API | Description |
---|---|
Controls whether the QueryResultsCache is enabled. |
|
Converts the id (or primary key) of an entity into a string representation (and back again). This SPI affects the representation of the identity of the domain entity, for example within a Bookmark. This id string also appears in URLs (of Wicket and REST API), and in mementos, for example of Commands and Interactions. The framework provides default implementations for all of the standard value types used for Ids. Applications should use this SPI to provide persistence of application-defined primary keys. (An example of this can be found in the Execution Log extension). |
Security SPI
The security SPIs influence how the framework handles various security concerns.
see also the Security Guide. |
API | Description |
---|---|
Create an audit record for every changed property of every changed object within a transaction. |
|
Records each login and logout by end-users. |
|
Create a new user account with the configured security mechanism. |
Public API vs Internal Services
The vast majority of Apache Causeway' domain services are defined in Apache Causeway' applib (o.a.c.core:causeway-applib
module) as stable, public classes.
Importantly, this also minimizes the coupling between your code and Apache Causeway, 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 Causeway includes an extensive number of domain services for your domain objects to use; simply define the service as an annotated field and Apache Causeway 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 LanguageProvider. The default implementation has these annotations:
@Service
@Named("causeway.runtimeservices.LanguageProviderDefault")
@Priority(PriorityPrecedence.MIDPOINT)
@Qualifier("Default")
@RequiredArgsConstructor(onConstructor_ = {@Inject})
public class LanguageProviderDefault
implements LanguageProvider {/* ... */ }
To override this, use something like:
@Service
@Priority(PriorityPrecedence.EARLY)
public class MyLocaleProvider implements LanguageProvider { /* ... */ }
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.
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.
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.