Kit is an all inclusive๐Ÿน web framework.

Build growth-ready applications fast, using powerful patterns.

โ€œOh no, why yet another framework?โ€ ๐Ÿคจ

A manifesto.
  1. We like Rails, but it does not teach you how to write extensible codeLink to this section

    Rails is interesting in many ways, but after you hit a controller, you're on your own. How the heck are you supposed to organize your code?

    In terms of reusability, Engines are a significant step, but how do you write an extensible one?

    We think many useful abstractions are missing, and that the ones provided often fall short. Sure, as with any technology, you can always make it work. But it will be time-consuming.

    We built Kit on top of Rails, and it is fully opt-in. When we can leverage what it does well, we gladly do. We use Engines, Railties, and ActiveRecord explicitly, and hide the rest behind connectors. You'll find that the reasons to interact with Rails disappear rapidly.

    The genuine question that led to Kit is: how do I write extensible code?

  2. DomainsLink to this section

    Domain Driven Design and particularly Bounded Contexts help a greatly to design your applications, reason about dependencies and write reusable code.

    Kit includes several ready-to-use domains. They are fully-fledged apps containing: the business logic for a specific part of the system, a ready-to-use JSON:API app, a user-facing web-app, and user-facing admin web-app.

    These domains can be deployed as standalone apps or included as libraries in other domains.

    Kit::Domain is here to help you build your own domains.

  3. Functional programmingLink to this section

    We think one reason Rails is not that extensible stems from relying heavily on mixins & inheritance. When you want to reuse a single utility method, you often have to instantiate or compose with a complex hierarchy of objects.

    This leads to code where the contracts are difficult to understand, and that is difficult to unit test.

    We try to avoid some shortfalls of OOP, and separate the code from the data. This helps greatly when reasoning about mutability and what you are actually trying to accomplish.

    We mostly use Modules with Class methods that you can call from anywhere, or plain lambdas.

  4. Railway oriented programmingLink to this section

    This lends itself very well to Railway oriented programming.

    Kit::Organizer helps you to write and chain code to compose any task. Think of it as a pipe operator.

  5. ContractsLink to this section

    Static typing is a powerful tool that will be part of Ruby 3.

    But as a programmer, what you actually want to do is enforce constraints like "this should be a positive int, lower than 10" or "this should be an array where the second index is a string with value X".

    In these situations, static typing falls short: you need dependent types (types that also include values). They often can not be solved at compile / transpile time, as the values are not known yet.

    As a result, you end up having to use several data validation primitives: one in the language that deals with types, and one in your project to validate your data (from a form, from a function parameter, etc.).

    We believe that there should only be one, opt-in, validation primitive.

    We add support for design by contract through Kit::Contract.

  6. Micro-services VS monolithLink to this section

    We believe the whole micro-services VS monolith debate is meaningless: the difference should only be a production detail.

    We use a single repo containing multiple domains and libraries, all set-up as independent gems. In effect: a monolith, where every domain is already designed as a micro-service.

    In production, you can expose these domains as one or several apps based on your needs, it makes no difference.

    We mount these domains in app containers thanks to Kit::AppContainer.

    Access from the outside world and inter-domains communication is handled by Kit::Router .

  7. About dataLink to this section

    We believe the data persistence layer leaks in too many applications, where it slowly becomes the app.

    We often access a table through different models based on what they represent in a given domain (here is an example).

    CQRS is a great pattern to deal with the reality of using various data sources.

    Most applications would benefit from using an immutable, append-only write store, and from using a snapshot of the current state of the data as a read store. Sadly, the tooling and vendor database support are not there yet.

    In the meantime, we can still be explicit about what model we use for reading or writing, and what store they should hit (Kit::Config makes this trivial).

    Even if the "append-only write store" can not be your data's source of truth, it can send events to external services (emailing, analytics, advertising, marketing automation, etc.), and if persisted you gain the ability to restore the system in a past state for audit.

The first usable release is a work in progress.

Here is an overview of the components we are working on and their current usability expressed as a percentage.