Callables
A Callable is a kind of Roli type that routes client calls to backend objects via code generated proxy classes. You can define a new Callable by deriving a class from either Endpoint
or Session
. Which base type you choose depends on what kind of logic you intend for it to contain.
Singletons
Callables are globally available Internet-connected singleton shared objects. This means that any number of client applications can call the same shared object instances at the same time. Callables are what developers typically think of as stateful microservices.
Remote Execution Model
Callables are ideal for housing code that needs to be run on the backend such as:
- Internal or proprietary business logic (game servers, user/account management, billing, etc..)
- Business rules specifying how data is saved, deleted, etc.
- Any logic that benefits from being co-located with the database (i.e. performance sensitive).
Properties
Callables may contain any number of TypeScript properties with any name. You can create new properties in any Callable method, not just the constructor, just like any other POJO.
Data Types
Most built-in JavaScript/TypeScript property types are supported including:
undefined
null
number
boolean
object
Map
Set
Array
Date
BigInt
- types that extend
Data
- types that extend
Endpoint
- types that extend
Session
Explicitly Deleted
Callables live in the backend database until you explicitly delete them. Callables cannot be deleted from client-side code.
Stateful
Callables retain the values of all their properties between calls. This makes them stateful.
When client A calls addUser('Scott')
if the user doesn’t already exist it will be saved and the endpoint’s this._usernames
is updated.
Later, when client B calls addUser('Scott')
the user will already exist and client B will get an error.
Call Semantics
When a client calls a method on a Callable reference, the request is sent over the Internet to the Roli backend where the Callable’s instance is hosted. The method is called on the Callable instance and if there wasn’t an error, the Callable’s changed properties are saved to the database.
Versioned
Callables are transparently versioned. Each time they’re changed and saved to the database the version changes. This version is used to ensure clients and services only work on the latest version of the Callable.
Automatically Saved
Any property changes made inside a Callable method call are saved automatically if the underlying call succeeds.
Implicitly Transactional
Each remote Callable method call is executed inside a database transaction. This means either all code inside the method works or no changes are made. This allows clients to retry calls without fear of data corruption.
Transaction Success or Failure
When the Callable method code is done executing, the following questions are asked to determine whether or not the transaction was successful:
- Was there a system-level error?
- Was an unhandled JavaScript exception thrown from user code?
- Were there any unsaved changes to
Data
instances? (E.g. Changeduser._firstName
but didn’t callsaveData(user)
) - Were there any concurrent, conflicting modifications to any objects modified by this transaction?
If the answer to any of these questions is yes, the transaction is rolled back causing all Data
and Callable modifications made as a result of this call to be undone. The client will receive an exception it can handle gracefully.
However, if the answer to all these questions is no, the transaction is committed, the Callable method call is successful, and the the Callable method’s return statement is returned to the client.
Reverting Callable Changes
Sometimes while inside a Callable method call you may want to revert all the changes made since the start of the transaction without throwing an exception. This can be useful if you want to handle a deterministic data condition silently, without concerning clients.
Deletion
Callables can be deleted from inside service code using the deleteObject
function.
The deleteObject
function leaves behind a flag in the database to prevent another object with the same type and key from being created. This design prevents accidental recreation. If you do not want this, pass true
as the second argument to deleteObject
.