npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@vitruvius-labs/architectura

v3.0.0

Published

A light weight strongly typed Node.JS framework providing isolated context for each request.

Downloads

268

Readme

Architectura

Architectura is a light-weight framework using no external dependency. It facilitates the implementation of a Domain-Driven Design software architecture. It provides a simple development API and blazing fast performances.

Forewords - Read first

Architectura is an opinionated, non-intrusive framework.

Opinionated

Architectura is opinionated because it follows and incites users to follow a Domain-Driven Design software architecture. It is also opinionated because it is dependency-free (outside of the Vitruvius Labs ecosystem) and enforces extremely strict typing. The opinion of the team behind Architectura is to provide a framework with premium quality standards. Architectura also follows the SOLID principles; however, we do not believe that dependency injection is the appropriate answer to the Dependency Inversion Principle. To that end, Architectura does not embark any dependency injection tool.

[!IMPORTANT] Note regarding dependency injection. Whilst you can add any dependency injection third party package, we invite you to reconsider before doing so. Dependency injection has many downsides and pitfalls that are often brushed over. This can rapidly make the technical debt of your project spiral out of control. We encourage you to learn how to implement dependency inversion in a more conventional, rigorous way than rely on magic. Again, Architectura does not enforce this idea on you, you are still free to use any package that you consider appropriate to your project.

Non-intrusive

Architectura is non-intrusive because you can easily onboard yourself on it and offboard.

Customers stick with terrible, non-functional solutions because they are prisoners of it. It is a form of sunk-cost fallacy. - Nicolas "SmashingQuasar" Lebacq, co-creator of Architectura

The objective is not to retain the user base by introducing unique patterns, a large number of dependencies or even an interlinking of services. The objective is to convince the user base that Architectura is the right fit for their project and needs. If they realise it wasn't due to their software needs sliding out of the scope of Architectura, they are not prisoners and can easily offboard themselves from it.

Getting started

To get started with Architectura, you do not need much. In fact, you need as little as this code:

import { Server } from "@vitruvius-labs/architectura";

const server: Server = await Server.Create({
	https: false,
	port: 80,
});

server.start();

It will create and start an HTTP server listening on port 80, but it won't do anything without endpoints.

[!IMPORTANT] Architectura supports native Node.js HTTPS servers. Always use HTTPS in production. This example is provided as-is for simplicity. Refer to the corresponding documentation to know how to setup your server.

Architectura does minimal work when it comes to the server. It uses the native Node.js server technology and wraps it within a unified Server class for simplification purposes.

Endpoints concept

Architectura differentiate itself from other framework by the use of endpoints. We think the MVC design pattern is often used improperly. Bloated controllers are far too common.

Architectura offers a self-contained way to express endpoints. When you declare an endpoint, all the informations necessary to its understanding is contained in a single file.

Here is an example of an endpoint handling a rudimentary application healthcheck:

import { type ExecutionContext, HTTPMethodEnum } from "@vitruvius-labs/architectura";
import { BaseEndpoint } from "@vitruvius-labs/architectura";

class HealthCheckEndpoint extends BaseEndpoint
{
	protected readonly method: HTTPMethodEnum = HTTPMethodEnum.GET;
	protected readonly route: string = "/health";

	public override async execute(context: ExecutionContext): Promise<void>
	{
		await context.getResponse().text("200 - OK");
	}
}

export { HealthCheckEndpoint };

As you can see, this endpoint is entirely self-contained. The route is directly visible as a property of the endpoint, as well as the HTTP verb (GET here). The content of the endpoint is also completely visible in one place.

[!IMPORTANT] This is a rudimentary example. Always take time to factor your code accordingly. Do not hesitate to add new methods to endpoints to make maintaining your code easier.

The basic concept of the endpoint in Architectura, is that it will always call the execute method that must be asynchronous. It will automatically map to the absolute route provided and use the corresponding HTTP verb. By using this approach, new developers can quickly understand what they are looking out with minimal cognitive load. It also has the benefit of making the removal of any route trivial. This is an important part regarding application security. Being able to safely remove a route without letting opened channels is a way to ensure you keep a clear understanding what you expose publicly.

Then you only need to add the endpoint to the endpoint registry to activate it. It can be done before or after starting the server, but we recommend doing it before.

import { EndpointRegistry } from "@vitruvius-labs/architectura";
import { HealthCheckEndpoint } from "./endpoint/healthcheck.endpoint.mjs";

EndpointRegistry.AddEndpoint(HealthCheckEndpoint);

If you want to know more about endpoints within Architectura, please refer to the Endpoints documentation.

Domains structure

Architectura articulates around the use of domains. As with most feature within Architectura, you are not forced to use domains. You are instead encouraged to do so to help structure your code base.

[!IMPORTANT] Explaining what a domain is is out of the scope of this documentation. If you would like to know more about domains in the context of Domain-Driven Design, please refer to the reference book from Eric Evans called Domain-Driven Design: Tackling Complexity in the Heart of Software.

Domains are classes that inherit from the BaseDomain class. (see src/ddd/base.domain.mts)

This class only serves as a basic structure and will allow Architectura to detect your domains at run-time.

[!NOTE] This would ideally be an interface instead of a class; however, TypeScript interfaces have no reality in JavaScript. To circumvent this limitation, the next most proper entity is a class.

Here is an example of a basic domain file that inherits from the BaseDomain class:

import { BaseDomain, EndpointRegistry } from "@vitruvius-labs/architectura";

class MyDomain extends BaseDomain
{
	public static override async Initialize(): Promise<void>
	{
		await super.Initialize();

		await EndpointRegistry.AddEndpointsDirectory(`${import.meta.dirname}/endpoint`);
	}
}

export { MyDomain };

On it's own, this class only load the domain endpoints in the sibling directory endpoint. It may include our previous health check endpoint.

If you are familiar with NestJS, domains are somewhat similar to modules. Within each of your domain, you should define the Initialize method. This method is used to ensure you have a centralised way to access anything needed for your code for the given domain.

Let's take an example.

[!NOTE] This will use an example naming convention, feel free to use any nomenclature you prefer.

If you work within a specific domain that embarks an entity called Dog that you will then stored inside a database. This entity will most likely have a model called dog.model.mts, a factory called dog.factory.mts and a repository called dog.repository.mts. It would make sense to create a GetDogFactory method on your domain to make sure the factory is accessed in a unified way everywhere within your code.

You can automate the loading of your domains. It can be done before or after starting the server, but we recommend doing it before.

import { DomainService } from "@vitruvius-labs/architectura";

await DomainService.LoadMultipleFromRootDirectory(`${import.meta.dirname}/domain`);

This code will load your domains in the domain sibling directory.

If you want to know more about domains within Architectura specifically, please refer to the Domains documentation.