Modules
Fantastic modules and where to find them
Module is a piece of functionality that is standalone and can be used based on a given settlement layer. It is a way of extending the functionality of your application in a settlement layer agnostic way. If you have an idea of something that needs coordination accross the whole stack: Backend, Frontend, CMS and UI - it's probably a good idea to create a fundset module for that. If you have an implementation that touches a single part of that stack e.g. a function callable only on backend that does something specific - it's probably better to make an npm package out of it.
Structure
Each module is split into the interface declaration and the implementation. So each module will consist of at least two files.
- Declaration file: in the
_fundset/settlement-layer/modulesdirectory, each module has to have a.d.tsfile that contains the extension of theSettlementLayerinterface, using tanstack-queryMutationOptionsandQueryOptionstypes. This part is settlement layer agnostic and is only defined once, even if you're gonna have different implementations for different settlement layers. This is an example definition from our counter module:
import { UseMutationOptions, UseQueryOptions } from '@tanstack/react-query';
export type GlobalCounterIncrementEvent = {
amount: number;
timestamp: Date;
by: number;
id: string;
};
export interface CounterModule {
counter: {
isIncrementGlobalCounterReady: boolean;
incrementGlobalCounterMutationOptions: () => UseMutationOptions<void, Error, number>;
globalCounterValueQueryOptions: () => UseQueryOptions<
unknown,
Error,
number | undefined,
QueryKey
>;
globalCounterIncrementEventsQueryOptions: ({
limit,
offset,
}: {
limit: number;
offset: number;
}) => UseQueryOptions<unknown, Error, GlobalCounterIncrementEvent[], QueryKey>;
isIncrementPersonalCounterReady: boolean;
incrementPersonalCounterMutationOptions: () => UseMutationOptions<void, Error, number>;
personalCounterValueQueryOptions: () => UseQueryOptions<
unknown,
Error,
number | undefined,
QueryKey
>;
};
}
declare module 'fundset/settlement-layer' {
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface SettlementLayer extends CounterModule {}
}For organisation purposes, you should group the module functions into a single object. In the example above, we're grouping the counter functions into a single object called counter, so e.g. functionality for getting the global counter value query options is available under settlementLayerObject.counter.globalCounterValueQueryOptions()
- Implementation files: each module also has an implementation that is tied to a specific settlement layer. It is located in the
_fundset/settlement-layer/modules/<module-name>/<settlement-layer-name>directory.
The implementation file should return either a direct implementation of the module definition or a function that is gonna build the module implementation based on context that is required for it to work. Which solution to use is up to you. Check out the docs on how to build your own module
Extending the Settlement Layer
Each settlement layer has its buildSettlementLayer function, which is responsible for constructing the settlement layer object and injecting implementation of the modules into it. That function is located in the _fundset/settlement-layer/<settlement-layer-name>/buildSettlementLayer.ts file.
In order to inject the module implementation into the settlement layer object, you need to pass it when constructing the settlement layer object:
// counter module is using a function to build the module implementation because it requires a user auth session
import type { authClient } from '@/lib/auth-client';
import { buildCounterModule } from '../modules/counter/pg/build';
export const buildPgSettlementLayer = ({
session,
}: {
session: ReturnType<typeof authClient.useSession>['data'];
}) => {
const pgSettlementLayer = {
name: 'pg',
...buildCounterModule({ session }),
};
return { pgSettlementLayer };
};After that you should be able to use the module in your application code by using the useSettlementLayer hook:
const {
counter: { incrementGlobalCounterMutationOptions },
} = useSettlementLayer();