This is Part 3 of 6 in the Let’s Explore ARK Core series which documents the development of the next major release of ARK Core, alongside some tips & tricks on how to get started with contributing and building your next idea today. Introduction In the and of this series, we gave a rough overview of how the Core infrastructure and application bootstrapping process was revamped. Today we’ll learn what inside of Core are, why they were introduced and what their job is to keep Core running. Part 1 Part 2 services ARK Core v3.0 Github Repository What is a service? Before starting we should set the stage by defining what a service inside of Core is. In simple terms, a service is a self-contained feature that only exposes a small chunk of the total business logic that makes up Core. For example, a log service, cache service, database service, transaction service, etc. — all of those are important building blocks of Core and should be easy to modify, maintain and test. Why were services introduced? Services aren’t actually all new in Core 3.0 but rather a rework of how certain plugins operated in Core 2.0. Core 2.0 had one major issue that was the result of the late container and plugin system introduction, it was so heavily fragmented to the point where plugins for functionality like loggers existed. You would always be required to include those plugins when bundling Core instead of them being part of a single package that you require in your own packages and have everything ready. required How were services introduced? In Core 3.0 we introduced a new package called which is the amalgamation of various packages that have previously existed in Core 2.0. Examples of those would be , , and more. core-kernel core-container core-logger core-event-emitter The package is the heart of Core 3.0 with the goal of resolving a lot of pain points from previous versions, improve DX, reducing boilerplate for package developers and lastly to reduce the fragmentation that plagues Core 2.0. core-kernel Part of is the directory which comes with a variety of services out of the box to reduce the boilerplate needed for the development of new packages and reduce duplication in existing ones. core-kernel services Actions Cache Config Events Filesystem Log Mixins Queue Schedule Validation We’ll go more in-depth into those services in a later part of this series. Registering your own services Registering your own services is as easy as can be and only takes a few lines of code due to the abstractions that come out of the box for package developers. Lets first have a look at the abstract that comes with Core to reduce the necessary boilerplate for service registrations. ServiceProvider () ServiceProvider { (Identifiers.Application) readonly app: Kernel.Application; packageConfiguration: PackageConfiguration; packageManifest: PackageManifest; register(): < >; boot(): < > { } dispose(): < > { } manifest(): PackageManifest { .packageManifest; } setManifest(manifest: PackageManifest): { .packageManifest = manifest; } name(): | { ( .packageManifest) { .packageManifest.get( ); } ; } version(): | { ( .packageManifest) { .packageManifest.get( ); } ; } config(): PackageConfiguration { .packageConfiguration; } setConfig(config: PackageConfiguration): { .packageConfiguration = config; } configDefaults(): JsonObject { {}; } configSchema(): object { {}; } dependencies(): Kernel.PackageDependency[] { []; } enableWhen(): < > { ; } disableWhen(): < > { ; } required(): < > { ; } } @injectable export abstract class /** * The application instance. */ @inject protected /** * The application instance. */ private /** * The loaded manifest. */ private /** * Register the service provider. */ public abstract async Promise void /** * Boot the service provider. */ public async Promise void // /** * Dispose the service provider. */ public async Promise void // /** * Get the manifest of the service provider. */ public return this /** * Set the manifest of the service provider. */ public void this /** * Get the name of the service provider. */ public string undefined if this return this "name" return undefined /** * Get the version of the service provider. * * @returns {string} * @memberof ServiceProvider */ public string undefined if this return this "version" return undefined /** * Get the configuration of the service provider. */ public return this /** * Set the configuration of the service provider. */ public void this /** * Get the configuration defaults of the service provider. */ public return /** * Get the configuration schema of the service provider. */ public return /** * Get the dependencies of the service provider. */ public return /** * Enable the service provider when the given conditions are met. */ public async Promise boolean return true /** * Disable the service provider when the given conditions are met. */ public async Promise boolean return false /** * Determine if the package is required, which influences how bootstrapping errors are handled. */ public async Promise boolean return false This is quite a bit of code so let's break it down into digestible parts. The abstract method is called by the bootstrapper classes that are responsible for registering services. This method should only register things, as its name indicates, and not start anything like HTTP servers. register The method is called by the bootstrapper classes that are responsible for booting services. This should act based on what happened in the method, i.e. start an HTTP server. boot register The method is called by the bootstrapper classes that are responsible for disposing of services. This should act based on what happened in the method, i.e. stop an HTTP server. dispose boot The method grants access to the of a package to gather information like name or version. manifest package.json The is called by the bootstrapper classes that are responsible for registering services. The file of a package will be automatically loaded, parsed and finally stored through this method. setManifest package.json The method returns the contents of the name property inside the file of a package. name package.json The method returns the contents of the version property inside the file of a package. version package.json The method grants access to the configuration of a package after it has been validated and normalized. config The method is called by the bootstrapper classes that are responsible for registering services. The configuration will be validated, normalized and finally stored through this method. setConfig The method is called by the bootstrapper classes that are responsible for registering services. The return value of this method will be merged with the user-supplied configuration to ensure all values are available. configDefaults The method is called by the bootstrapper classes that are responsible for registering services. This method has to return a schema that will be used to validate and normalize the configuration. configSchema @hapi/joi The method is called by the bootstrapper classes that are responsible for registering services. The return value of this method has to be an array of objects that contain information like names and version constraints. dependencies The methods are called by the bootstrapper classes when a block is applied and are responsible for (de)registering services. These methods have to return a boolean value that determines when to enable or disable a service. enableWhen / disableWhen The method is called by the bootstrapper classes that are responsible for registering services. This method has to return a boolean value that determines whether or not a service is required. Required plugins receive stricter error handling and any errors during registration or booting result in process termination. required That’s the functionality a service provider comes with out of the box but the only methods you’ll interact with in most cases are and . Let us take a look at an example service provider to illustrate their use. register, boot dispose { Providers } ; { Server } ; ServiceProvider Providers.ServiceProvider { register(): < > { .app.bind<Server>( ).toConstantValue( Server()); } boot(): < > { .app.get<Server>( ).start(); } dispose(): < > { .app.get<Server>( ).stop(); } } import from "@arkecosystem/core-kernel" import from "@hapi/hapi" export class extends public async Promise void this "api" new public async Promise void await this "api" public async Promise void await this "api" The method binds a new instance of a hapi.js server to the container without starting it. register The method retrieves the previously registered server from the container and calls the start method on it. boot The method retrieves the previously registered server from the container and calls the stop method on it. dispose As you can see it’s pretty easy to register your own services without much hassle and everything is clearly named, let's end with listing some of the benefits of this new architecture compared to Core 2.0. Benefits of new service provider lifecycle Clear separation of responsibilities during application bootstrap.Easy testing due to a clear separation of responsibilities. Ability to enable and disable packages at runtime without a full teardown. The by far biggest benefit for package developers is that it is now possible to alter or extend other packages due to how the application bootstrapping now works. An example of this would be a plugin that adds new routes or plugins to the package before the server is started, all it would take is to resolve the hapi.js server from the container in the method and call the usual methods provided by hapi.js on the resolved value. core-api register This removes the need to spin up your own HTTP server if you just need 1–2 extra API endpoints. Being able to do modify other plugins before they are launched will provide developers with greater control and possibilities to modify how core behaves. What’s next? This concludes Part 3 of the Let's Explore ARK Core series. In the next part, we will delve into how ARK Core 3.0 is more extensible than ever and how you can take advantage of this to reduce your time spent on developing packages. I Want To Help With Development That's great news! If you want to help out, are wide open, but that is not all, we also have special Monthly Development GitHub Bounties on-going where you can earn money for every valid and merged Pull-Request. our GitHub repositories To learn more about the program please read our blog post and to learn more about ARK Core and blockchain visit our . Bounty Program Guidelines Learning Hub