diff --git a/website/.vitepress/config.mts b/website/.vitepress/config.mts index 8448832..cbeb415 100644 --- a/website/.vitepress/config.mts +++ b/website/.vitepress/config.mts @@ -20,6 +20,14 @@ export default defineConfig({ text: "Domains", link: "/domains", }, + { + text: "Layers", + link: "/layers", + }, + { + text: "Layer Groups", + link: "/layer-groups", + }, ], socialLinks: [{ icon: "github", link: "https://github.com/pwlmc/morando" }], }, diff --git a/website/docs/layer-groups.md b/website/docs/layer-groups.md new file mode 100644 index 0000000..28dd650 --- /dev/null +++ b/website/docs/layer-groups.md @@ -0,0 +1,47 @@ +# Layer Groups + +## Layers Belong to Groups + +The idea of a layer group comes from a simple observation: all code in modern +front-end applications can be grouped into four broad levels of abstraction. +They are: + +| Layer Group | Description | Default layer | +| ------------ | --------------------------------------------------------------------------------------------------------------------------------------- | ------------- | +| Definitions | The most abstract layer group. It contains foundational, reusable types, and invariants that are independent of implementation details. | `defs` | +| Operations | The business logic layer. It holds operations, services, and utilities that do not involve presentation logic. | `ops` | +| Presentation | The user interface layer. It contains components, views, and UI-specific code that present data and interact with users. | `ui` | +| Bootstrap | The application entry layer. It wires together lower layers and bootstraps the app. | `main` | + +Every Morando project needs to define four layers at minimum: at least one layer +in each of the four layer groups. + +## Default Layers + +Each layer group needs to have at least one layer defined. For convenience, +Morando provides predefined default layers - one for each group. Those layers +are: `defs`, `ops`, `ui`, and `main`. + +Projects are free to customize their list of layers, but this is not standard +practice. Morando comes with predefined layer lists for the most popular +front-end frameworks. Choosing one that fits your project's tech stack should +remove the need to define custom layers altogether. + +## Custom Layers Example + +For educational purposes, let's pretend we want to define a custom layer list +for our React single-page web application. In such an application, it makes +sense to define an additional `hooks` operations layer in addition to the +default `ops` layer. We might also want to have more specialized presentation +layers instead of a single general `ui` layer. Let's say we want our +presentation layers to consist of `components` and `pages`. The final list of +layers could then look something like this (from lowest to highest): + +| Layer name | Layer Group | +| ------------ | ------------ | +| `defs` | Definitions | +| `ops` | Operations | +| `hooks` | Operations | +| `components` | Presentation | +| `pages` | Presentation | +| `main` | Bootstrap | diff --git a/website/docs/layers.md b/website/docs/layers.md index 3963fac..32a7d5f 100644 --- a/website/docs/layers.md +++ b/website/docs/layers.md @@ -1,3 +1,78 @@ # Layers -TBA +## Modules vs. Layers + +While modules are specific to your project, layers are closely related to the +tech stack. + +Every project that uses Morando architecture will inevitably contain different +modules. That is expected, because modules express the application's business +domain. You cannot expect an online banking app to have the exact same set of +modules as, for example, a hotel-booking app. Some modules may be similar, for +instance, both may include a `@/Routing` module, but the core business modules +will still diverge. + +Layers are different. In all Morando applications, you will find some universal +base layers that are always present. In many apps, the list of layers will be +identical. That makes it much easier to switch between projects when you have a +consistent set of layers to anchor yourself to. Let's define what layers are so +we can see how this emerges. + +## What Are Layers? + +A layer is simply a name that represents a level of abstraction in a Morando +project. + +The lowest layers are those that contain the most core and abstract definitions +on top of which you want to build the rest of your application. The higher the +layer, the more implementation-specific and less abstract it becomes. + +For each project, the list of layers needs to be defined upfront because without +it we can't validate the project's architecture. More details on what a concrete +list of layers might look like will be discussed in the +[Layer groups](./layer-groups.md) chapter. For now, it is enough to assume that +layers are an ordered list of names defined for a Morando project. + +## Files Are Classified to Layers + +In Morando architecture, every file must be classified to a layer. + +A file is assigned to a layer by a classifier function. Because Morando never +parses a file's code, the classifier usually tries to assign a file based on its +location in the project, file name, extension, and so on. For example, in a +React project that defines a `components` layer, a `Button.tsx` file may be +classified to that layer because it starts with an uppercase letter and has a +`tsx` extension. + +The classifier's algorithm is an implementation detail specific to the concrete +list of layers, and we will not discuss it here. That said, there is one +universal classifier for all Morando projects, already mentioned in the +[Modules](./modules.md#modules-are-flat) chapter. + +Layer folders are special folders named exactly as the layer. They are optional +folders that can be placed inside a module and act as classifiers for all files +inside them. For example, in the case of the following hypothetical module: + +```text +Shared/ +└── components/ + ├── foo.ts + └── bar.ts +``` + +`foo.ts` and `bar.ts` would be classified to the `components` layer. Of course, +that assumes the project defines the `components` layer; otherwise, the +`components/` folder would not be a valid layer folder. + +## The Golden Rule of Layers + +Before we move forward, we need to discuss one important rule that makes layers +so important for a project's stability and validity. + +:::tip Layers Golden Rule +A file can depend only on files from the same or lower layer. +::: + +The rule is global, which means it does not matter what module the file is +located in. As long as the file is not importing a file that is classified to a +layer higher than its own, the rule is not violated. diff --git a/website/docs/layers_old.md b/website/docs/layers_old.md deleted file mode 100644 index aacae85..0000000 --- a/website/docs/layers_old.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -sidebar_position: 3 ---- - -# Layers - -Layers are the architectural backbone of Morando architecture. They help define -how your code is organized, who can depend on whom across modules. If you want -maintainable boundaries and predictable module relationships, layers are the -tool for the job. - -## What Is a Layer? - -A layer is simply a name that represents a level of abstraction in your project. -Examples might include: core, domain, features, or ui. - -Each project using this architecture must define its own **ordered list of -layers** upfront. This order matters because it establishes allowed directions -of dependencies. In short: - -- Higher layers can depend on lower ones. -- Lower layers cannot depend on higher ones. - -Without this layered structure in place, there’s no way to enforce architectural -boundaries or detect invalid dependencies. - -### Layer Representation - -In this system, **every single-file module must belong to a layer**. - -When a file is assigned to a layer, we say it **represents** that layer. These -files are the **layer representatives** and they serve as the smallest, most -atomic participants in layer validation mechanism. - -
-How Is a File Assigned to a Layer? - -By default, the system uses a classifier defined in the project template based -on file or folder naming convention (more on Templates later). Ideally, it -should work well for most of the use cases and remove the need for manual -tagging to zero. - -However, you can also define **custom classification rules** at the project -level. For example, you might want to classify files by filename pattern (e.g., -\*.service.ts → domain layer). These custom rules override the default behavior -and allow you to make the architecture truly your own. It may also come in handy -during the transition periods, for example while the project is gradually -updated to comply with the Modando architecture. -
- -For **folder modules** we determine their layer representation by the **highest -layer represented by their submodules**. For example, the following folder -module: - -``` -. -└── MyModule -    ├── firstLayerModule.ts - └── SecondLayerFile.ts -``` - -Assuming that: - -- `firstLayerModule.ts` represents `first` layer -- `SecondLayerFile.ts` represents `second` layer -- In the list of layers `second` is higher than `first` - -Then `MyModule` represents `second` layer. - -:::info Notice that it's relatively easy to change the folder module layer by -moving the submodules around. ::: - -## The Golden Rule of Layers - -To keep your architecture clean, there’s one core rule you need to follow: - -:::tip A module can only depend on the same layer module or module representing -lower layers. ::: - -The rule is **strict** and **global**: - -- It doesn’t matter is the two dependant modules have the same ancestor or not. -- **Importing upward** in the layer hierarchy is always considered invalid. diff --git a/website/docs/modules.md b/website/docs/modules.md index 2794393..cb407a5 100644 --- a/website/docs/modules.md +++ b/website/docs/modules.md @@ -15,10 +15,10 @@ contain source code files. Below you can find an example of a `User` module: ```text User/ -├── UserAvatar.tsx -├── useUser.ts +├── model.ts ├── api.ts -└── model.ts +├── useUser.ts +└── UserAvatar.tsx ``` ## Modules Are Flat @@ -30,9 +30,9 @@ their names are strictly tied to the layers defined in your project. They are _layer folders_, and to make them easier to tell apart from modules, their names start with a lowercase letter. -We will cover them in the [Layers](./layers.md) chapter, but for this chapter, -it is enough to remember that they are optional. Like modules, layer folders -also cannot be nested. +We will cover them in the [Layers](./layers.md#files-are-classified-to-layers) +chapter, but for this chapter, it is enough to remember that they are optional. +Like modules, layer folders also cannot be nested. Below is an example of a `Shared` module that contains `utils` and `components` layer folders.