Create a use case
A use case is a piece of business functionality. It takes an input, processes it through lifecycle methods (client and/or server), and gives an output.
In this documentation and in the code, use case is often refered to as UC
or uc
.
Since it's a notion widely used in libmodulor
, the abbreviation makes sense to make things more concise.
Thus, UCD
stands for Use Case Definition
, UCIF
stands for Use Case Input Field
and so on.
Metadata
A use case is declared by defining its metadata in the app's manifest
as seen in Create an app.
The metadata defines several properties that are used by the app, products and targets to instrument the use case.
The action
can be used by a server target to decide which HTTP verb to use.
The icon
can be used by a GUI to display it alongside the label in a button.
You are free to set the icon of your choice, corresponding to the icon library you're using in your GUI target (e.g. FontAwesome).
The beta
and new
flags can be used by a GUI target to display a badge next to the button giving access to the use case, in order to show the user that it's a new feature.
The sensitive
flag can be used to ask the user to confirm when they execute a use case (e.g. a destructive use case).
UCD => Use Case Definition
A use case is defined in a file named src/apps/{AppName}/src/ucds/{UCName}UCD.ts
.
It must export :
- the interface corresponding to the input (if any)
- the interface corresponding to the OPIs (if any)
- a const named
{UCName}UCD
that satisfies theUCDef
interface.
IO => input/output
A use case can define 0 or 1 input and 0, 1 or 2 output part items (OPI). Based on this, the definition of a use case has one of the following types :
I => input
It is an interface that must extend the UCInput
interface directly or indirectly.
It must contain only properties of type UCInputFieldValue<DataType>
.
You can use one of the existing data types or create a data type.
The same fields must be declared in UCDef.io.i.fields
.
This definition might seem redundant but it offers more expressiveness to define advanced rules for data types than the simple TypeScript type system.
O => output
It is an interface that must extend the UCOPIBase
interface directly or indirectly.
The same fields must be declared in UCDef.io.o.parts._0
.
Lifecycle
A use case can define a client
and/or a server
lifecycle.
client
It is an object that satisfies the UCClientDef<I, OPI0, OPI1>
interface.
The main
property references a class that implements the UCMain<I, OPI0, OPI1>
interface.
This class can be defined just above the UCDef
like the following :
In this class, you can inject whatever you want and define all your client side business logic in exec
.
In this specific case, we are simply sending the use case to the server.
The policy
defines whether the current user can see and/or execute the use case.
You can use one of the existing policies or create a policy.
server
It is an object that satisfies the UCServerDef<I, OPI0, OPI1>
interface.
The main
property references a class that implements the UCMain<I, OPI0, OPI1>
interface.
This class must be defined in a dedicated file src/apps/{AppName}/src/ucds/{UCName}ServerMain.ts
like the following :
Using a comment starting with // >=>
in ClientMain
and ServerMain
has a specific meaning as we'll see in Test the app.
The init
property references a class that implements the UCInit
interface.
It allows you to setup stuff when the use case is mounted (e.g. creating some caches or data stores when mounting the use case on a server target).
The policy
defines whether the current user can execute the use case.
You can use one of the existing policies or create a policy.
Metadata
As seen above, the metadata is declared in the app manifest. In the UCD, you simply need to reference this declaration.
Full definition
All in all, a typical use case definition looks like this :
Advanced
The UCDef
interface allows to define much more properties like ext
and sec
or sideEffects
. These are more advanced topics and will be addressed in dedicated guides.