This is Part 2 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 of this series, we gave a rough overview of how the Core infrastructure was revamped to allow for greater control over multiple processes. Today, we’ll continue by taking a closer look at how the application is bootstrapped and what role events play in this. Part 1 ARK Core v3.0 Github Repository Bootstrapping In Core v2, the bootstrapping process was very basic and difficult to adjust. It consisted of a single method and an accompanying plugin loader class that contained all logic to register and boot things. This was bad for 2 reasons. Testing was very difficult as those things were hardcoded in the depths of the application object with no way of easily accessing them. All logic was centralized in a single entity which meant that every small change could break anything and disabling certain steps for testing was not possible. Core v3 tackles the above issues by introducing a new bootstrapping implementation. Every step that is required for Core to get up and running is split into a bootstrapper class that is responsible for a single task. Those bootstrapper classes only expose a single public method that is called by Core to execute the task it is responsible for. bootstrap Let's have a closer look at what bootstrappers are available when they are executed and what they do. Available Bootstrappers Currently, a handful of bootstrappers are used within Core to break down the application start into small pieces with as little responsibility as possible to make testing and finding problems within them easier. RegisterErrorHandler Bootstrapper RegisterBaseConfiguration Bootstrapper RegisterBaseServiceProviders Bootstrapper RegisterBaseBindings Bootstrapper RegisterBaseNamespace Bootstrapper RegisterBasePaths Bootstrapper LoadEnvironmentVariables Bootstrapper LoadConfiguration Bootstrapper LoadCryptography Bootstrapper WatchConfiguration Bootstrapper LoadServiceProviders Bootstrapper RegisterServiceProviders Bootstrapper BootServiceProviders Bootstrapper // Application class implements class implements class implements class implements class implements class implements class implements class implements class implements class implements // Service Providers class implements class implements class implements As you can see, all classes implement the contract which lets Core know that this class contains a method that should be executed. The order in which the bootstrappers are included and executed matters as they are all responsible for taking care of small tasks, for example, trying to register a plugin before its configuration is loaded doesn’t make sense. Bootstrapper bootstrap Application Start In Core v3, the starting of the application is split into 2 steps, and . Let's take a look at what those methods do and when they are called. bootstrap boot Bootstrap The first step is to bootstrap the application which takes care of 3 steps in preparation for the execution of bootstrapper classes. The event dispatcher is registered as the first service as it is needed early on in the application lifecycle. The initial configuration and CLI flags are stored in the container to make them easily accessible during the lifecycle of the application. The is registered which is a repository that holds the state of registered services through plugins. Those states are registered, loaded, failed and deferred; more on those in a later part. ServiceProviderRepository bootstrap(config: JsonObject): < > { .registerEventDispatcher(); .container .bind<JsonObject>(Identifiers.ConfigBootstrap) .toConstantValue(config); .container .bind<ServiceProviderRepository>(Identifiers.ServiceProviderRepository) .to(ServiceProviderRepository) .inSingletonScope(); .bootstrapWith( ); } public async Promise void await this this this await this "app" The method accepts a single argument which lets it know what bootstrappers should be executed. This is important so the application is only bootstrapped but not actually started, think of it as packing everything into your car for your vacation but not yet starting to drive. Everything is ready and just waiting for you to turn the keys and start. bootstrapWith Boot The second step is to boot the application. This will call the and methods on service providers that are exposed by packages that were discovered during the bootstrap process. register boot boot(): < > { .bootstrapWith( ); .booted = ; } public async Promise void await this "serviceProviders" this true Again, we are calling the method but this time with as the argument. This will let Core know to run the bootstrappers that are responsible for registering and booting packages through their exposed service providers. bootstrapWith serviceProviders The bootstrapWith method As we have seen in the previous section, the method is at the heart of getting the application up and running. Let's break down the following code snippet to get a better idea of what is happening. bootstrapWith private bootstrapWith(type: string): < > { bootstrappers: <Constructor<Bootstrapper>> = .values(Bootstrappers[type]); ( bootstrapper bootstrappers) { .events.dispatch( , ); .container.resolve<Bootstrapper>(bootstrapper).bootstrap(); .events.dispatch( , ); } } async Promise void const Array Object for const of this `bootstrapping: ` ${bootstrapper.name} this await this this `bootstrapped: ` ${bootstrapper.name} this We get a key-value pair of available bootstrappers, currently, only and exist. app serviceProviders We loop over all available bootstrappers. We fire a event before executing the bootstrapper. This will look something like . bootstrapping:{name} bootstrapping:LoadEnvironmentVariables We resolve the bootstrapper class from the container and call the method to execute its task. bootstrap We fire a event after executing the bootstrapper. This will look something like . bootstrapped:{name} bootstrapped:LoadEnvironmentVariables As you can see Core dispatches an event for every bootstrapper that is executed. This is useful for internal tasks that are performed outside of bootstrappers without needing any hacks as they can rely on the event-driven architecture of the bootstrapping feature. Event Dispatcher You might have noticed in the previous example that the event dispatcher no longer provides an or method. This is due to a complete replacement of the native event dispatcher with our own implementation that provides more features to aid the use of an event-driven architecture for certain features. on emit First, let's have a look at the implementation contract that is specified within . This contract needs to be satisfied by all event dispatcher implementations, core ships with an in-memory solution by default but something like Redis should be easy enough to implement as an alternative. core-kernel EventDispatcher { listen(event: EventName, listener: EventListener): ; listenMany(events: <[EventName, EventListener]>): Map<EventName, >; listenOnce(name: EventName, listener: EventListener): ; forget(event: EventName, listener?: EventListener): ; forgetMany(events: <[EventName, EventListener]>): ; flush(): ; getListeners(event: EventName): EventListener[]; hasListeners(event: EventName): ; dispatch<T = >(event: EventName, data?: T): < >; dispatchSeq<T = >(event: EventName, data?: T): < >; dispatchSync<T = >(event: EventName, data?: T): ; dispatchMany<T = >(events: <[EventName, T]>): < >; dispatchManySeq<T = >(events: <[EventName, T]>): < >; dispatchManySync<T = >(events: <[EventName, T]>): ; } export interface /** * Register a listener with the dispatcher. */ => () void /** * Register many listeners with the dispatcher. */ Array => () void /** * Register a one-time listener with the dispatcher. */ void /** * Remove a listener from the dispatcher. */ void /** * Remove many listeners from the dispatcher. */ Array void /** * Remove all listeners from the dispatcher. */ void /** * Get all of the listeners for a given event name. */ /** * Determine if a given event has listeners. */ boolean /** * Fire an event and call the listeners in asynchronous order. */ any Promise void /** * Fire an event and call the listeners in sequential order. */ any Promise void /** * Fire an event and call the listeners in synchronous order. */ any void /** * Fire many events and call the listeners in asynchronous order. */ any Array Promise void /** * Fire many events and call the listeners in sequential order. */ any Array Promise void /** * Fire many events and call the listeners in synchronous order. */ any Array void Already, at first sight, you’ll notice that our own event dispatcher provides a lot more methods than the default node.js event dispatcher does. The biggest benefit of this new event dispatcher is that it has built-in support for asynchronous dispatching of events. Core v2.4 started to make use of a few internal events in to decouple certain tasks like banning and disconnecting a peer. Previously, tasks of that nature were just thrown in wherever they fit best at the time rather than being placed in an entity where they actually belong to. core-p2p This new event dispatcher will provide us the tools we need to make more use of an event-driven architecture to help loosen up coupling and make testing even easier as a result of that. This will be an ongoing process as there will always be room for improvements or a performance gain by dispatching an event and executing all its tasks in an asynchronous manner while waiting for the result without blocking the main thread. What’s Next? This concludes Part 2 of the ARK Core Adventures series. In the next part, we will delve into how essential parts of the system are split into services in ARK Core v3 to provide a robust and testable framework for the future. 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. Bounty Program Guidelines Previously published at https://blog.ark.io/lets-explore-core-v3-part-2-bootstrap-events-f24adf70dfff