Services
Services enable modules to inject custom logic into JDito processes without overriding them directly. They complement Extension Points (for data model extensions) and are ideal for reusable logic that must run across multiple modules or at specific runtime hooks such as insert, update, or delete.
Services are the safest way to add and preserve new functions and their configurations.
There are two service scopes:
- Global Services: Available throughout the project
- Entity Services: Scoped to a specific entity and only available there
Before creating a new service, you should consider if this service is needed in only one entity or in multiple global processes or entities. If your service is only needed in one entity, consider using an Entity Services. Otherwise, you need to use a Global Service.
Terms
- Service Definition: A named collection that groups multiple service implementations. This name is used to load all implementations.
- Service Implementation: A single piece of JDito logic that belongs to a service definition.
Benefits
- Encapsulates reusable logic across modules
- Reduces the need for process overrides
- Provides clean injection points in shared processes
- Enables dynamic extension without breaking modular separation
Typical use cases:
- Any process logic where multiple modules might need to contribute to.
onDBInsert,onDBUpdate,onDBDelete- Dependency calculation (e.g.,
Dependency.mapping) - Validation, enrichment, and transformation
Defining and Implementing Services
Global Services
Create global services under the service node in the Designer.

Figure: Global Service with two implementations
Entity Services
Entity Services are defined within an entity and are only available there. Creation and implementation follow the same procedure as Global Services.

Figure: Multiple Entity Services inside a specific entity
Creating a Service Definition
- Right-click the
servicefolder → "New" - Select type
serviceand use a name using the patternlowerCamelCase_service
The Service Definition is now created and can be referenced in the process.
Consider adding documentation to the Service Definition describing how it should be used across different packages.
Adding an implementation
You do not need to add an implementation in the same module as the service definition. If you want, you can add an implementation in the same module as the service definition.
You should be aware that service implementations can be added from other modules.
Steps:
- Right-click on the desired service → Add Implementation
- Name the implementation using the pattern
lowerCamelCase_impl - Provide the logic in the process property
ESLint or TypeScript may warn about the returned function. If necessary, place //@ts-ignore directly before return.
Calling Services
Use modules.loadService("serviceName_service") to retrieve all implementations.
This use case is the same for Global and Entity Services.
Example: Calling a Service with no parameters
import { modules } from "@aditosoftware/jdito-types";
modules.loadService("serviceName_service").forEach(impl => impl());
At transpile time, each implementation becomes a generated function. The transpiler replaces loadService(...) with an array of those functions.
The generated result during transpilation:
import { modules } from "@aditosoftware/jdito-types";
[_____GENERATED_serviceName_service_first_impl, _____GENERATED_serviceName_service_second_impl].forEach(impl => impl());
function _____GENERATED_serviceName_service_first_impl()
{
console.log('hello from first implementation');
}
function _____GENERATED_serviceName_service_second_impl()
{
console.log('hello from second implementation');
}
As you can see, all generated functions are added to your process.js file with its content.
Example: Calling a service with parameters
If implementations need parameters, each implementation must return a function which is then invoked with the parameters.
import { modules } from "@aditosoftware/jdito-types";
modules.loadService("myService_service").forEach(impl => impl()(param1, param2));
Implementation:
let fn = (p1, p2) => {
// logic
};
//@ts-ignore
return fn;
Usage Example of Services: AttributeUtil
This example demonstrates the benefits of using Services in a process. You can find this pattern in any xRM project.
Before Modularization
Previously, Attribute_lib contained a single array with getPossibleUsageContexts. This contained all contexts that are usable for attributes.
Whenever a new suitable context was added, the function had to be edited.
AttributeUtil.getPossibleUsageContexts = function()
{
return [
"Organisation",
"Person",
"Contract",
"Product",
"Activity",
"Offer",
"Order",
"Employee",
"Salesproject",
"Campaign",
"DocumentTemplate",
"SupportTicket",
"Leadimport",
"ImportField",
"DSGVO"
];
}
After Modularization
The return function is now a simple call to modules.loadService and a simple object merge.
AttributeUtil.getPossibleUsageContexts = function()
{
return modules.loadService("attributeUsageContexts_service")
.reduce((contextAccumulator, nextContextsFn) => contextAccumulator.concat(nextContextsFn()), []);
};
The global service attributeUsageContexts_service contains all implementations with possible contexts, and each module contributes any number of implementations that return their portion of the mapping.

Figure: Service Implementation for attributeUsageContexts_service
A single implementation looks like this:
//@ts-ignore
return [
"Offer"
];
As a result, no one needs to modify the lib, but can contribute a Service Implementation to add a new context. Each module can contribute its own implementation independently.