@honout/functionality
v0.0.7
Published
#### Creating a custom functionality
Downloads
5
Maintainers
Readme
Honout Functionality
Creating a custom functionality
Start by importing the IFunctionality interface from @honout/system
import { IFunctionality } from '@honout/system';
The interface has 3 generic parameters.
- ExtendableLogic
- Internal logic interfaces that may be extended
- LogicIdentifiers
- Identifiers to the above.
- Configuration
- Configuration of this functionality
IFunctionality<ExtendableLogic, LogicIdentifiers, Configuration>;
Next create a class that implements this interface and mark it as injectable. As an example, let´s create a functionality for event handling. First declare an interface for your event handlers.
interface IEventHandler {
// called when an event is received
onEvent(message: string): void;
// the type of event this handler should listen to
getType(): string;
}
Next, create your publicly extendable logics. In this case, we only want to be able to add event handlers so this interface will be the only logic extension.
type ExtendableLogic = IEventHandler;
Next define your public logic identifiers.
enum MyFunctionalityIdentifiers {
EVENT_HANDLER = 'IEventHandler',
}
If your functionality may be configured, declare an interface for the configuration also
interface IConfiguration {
foo: string;
}
Finally we want to create a separate interface that will be used to consume all event handlers and to start actually listening to events
interface IEventManager {
start(): void;
}
Now that all of our types and interfaces have been declared, let´s piece the logic together. Let´s create a class that implements the IFunctionality interface.
@injectable()
class EventFunctionality
implements
IFunctionality<
ExtendableLogic,
MyFunctionalityIdentifiers,
IConfiguration
>
{
// Receives any extensions, it is up to each functionality to handle this.
onLogicExtensions(
extensions: ILogicExtension<
ExtendableLogic,
MyFunctionalityIdentifiers
>[],
container: Container
): void {}
// Called with the configuration given to this functionality
onConfigure(configuration: IConfiguration): void {}
// Called when it is time for this functionality to bind its´ internal dependencies.
bindInternals(container: Container): void {}
// This will be called when all of the above has been resolved
async start(container: Container): Promise<void> {}
}
Lets start by implementing some logic in these methods. Lets begin with onLogicExtensions
onLogicExtensions(
extensions: ILogicExtension<ExtendableLogic, MyFunctionalityIdentifiers, IConfiguration>[],
container: Container
): void {
// we loop through each possible extension even though we only have one in this case, the IEventHandler
extensions.forEach((extension) => {
// We use the identifier to verify the type of extension
if (extension.identifier === MyFunctionalityIdentifiers.EVENT_HANDLER) {
// And finally we go thorugh all the IEventHandlers and bind them to the di container
extension.definitions.forEach((definition) => {
container
.bind(extension.identifier)
.to(definition)
.inSingletonScope()
})
}
})
}
Before we implement the bindInternals
method, we need to implement the IEventManager since bindInternals
will use this dependency. So let´s to that.
@injectable()
class EventManager implements IEventManager {
constructor(
// Inject all event handlers
@multiInject(MyFunctionalityIdentifiers.EVENT_HANDLER)
// The number of event handlers could be 0, meaning we declare this as optional
@optional()
private eventHandlers: IEventHandler[]
) {}
start() {
// Go through each injected event handler
this.eventHandlers.forEach((eventHandler) => {
// Bind the handler to the emitter
EventEmitter.on(eventHandler.getType(), eventHandler.onEvent);
});
}
}
Now we can finally implement the bindInternals
method
bindInternals(container: Container): void {
container.bind<IEventManager>('IEventManager').to(EventManager).inSingletonScope()
}
And lastly, let´s implement the start
method
async start(container: Container): Promise<void> {
container.get<IEventManager>('IEventManager').start()
}
Let´s use this functionality in an application now. First we need to create an event handler
@injectable()
class MyEventHandler implements IEventHandler {
onEvent(message: string): void {
console.log('Message received', message);
}
getType(): string {
return 'message';
}
}
In order to add this to an application, there is a decorator available from the @honout/system
that we can use
import { WithFunctionality } from '@honout/system';
@injectable()
@WithFunctionality({
functionality: EventFunctionality,
extend: [
{
identifier: MyFunctionalityIdentifiers.EVENT_HANDLER,
definitions: [MyEventHandler],
},
],
})
class MyApp implements IApplication {}
And that´s it. When the System starts, this functionality will automatically run and starts listening to 'message' events.