Skip to content

Commit

Permalink
docs: add vitepresst
Browse files Browse the repository at this point in the history
  • Loading branch information
gtoselli committed Apr 25, 2024
1 parent e3bcccc commit 3dc67d1
Show file tree
Hide file tree
Showing 12 changed files with 1,361 additions and 4 deletions.
61 changes: 61 additions & 0 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Sample workflow for building and deploying a VitePress site to GitHub Pages
#
name: Deploy VitePress site to Pages

on:
push:
paths:
- 'docs/**'
# branches: [ main ]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v3
with:
version: latest
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Install dependencies
run: pnpm install
- name: Build with VitePress
run: npm run docs:build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: docs/.vitepress/dist

deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
name: Deploy
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
6 changes: 5 additions & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@

name: Node.js CI

on: push
on:
push:
paths:
- '!docs/**'


jobs:
build:
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,7 @@ coverage
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/extensions.json

docs/.vitepress/dist
docs/.vitepress/cache
49 changes: 49 additions & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { defineConfig } from 'vitepress';

// https://vitepress.dev/reference/site-config
export default defineConfig({
title: 'Ddd toolkit',
description: 'Simple ddd stuffs',
themeConfig: {
// logo: '/assets/logo.jpeg',
// https://vitepress.dev/reference/default-theme-config
nav: [
{ text: 'Home', link: '/' },
{ text: 'Examples', link: '/examples' },
],

sidebar: [
{
text: 'Getting Started',
link: '/getting-started',
},
{
text: 'Components',
items: [
{ text: 'Aggregate repo', link: '/aggregate-repo' },
],
},
],

socialLinks: [
{ icon: 'github', link: 'https://github.com/fizzbuds/ddd-toolkit' },
],

footer: {
copyright: 'Copyright © Gabriele Toselli, Luca Giovenzana and contributors.',
},

lastUpdated: {
text: 'Updated at',
formatOptions: {
dateStyle: 'full',
timeStyle: 'medium',
},
},

editLink: {
pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path',
text: 'Edit this page on GitHub',
},
},
});
124 changes: 124 additions & 0 deletions docs/aggregate-repo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Aggregate repo

## Aggregate

In Domain-Driven Design (DDD), an aggregate is a cluster of domain objects that can be treated as a single unit. It acts
as a transactional boundary, ensuring consistency and integrity within the domain model. Aggregates encapsulate the
invariant rules that govern the interactions among their constituent objects, allowing for a clear and manageable domain
structure. By defining boundaries around related entities, aggregates promote better organization, scalability, and
maintainability of the domain model, facilitating easier development and evolution of complex systems.

### Aggregate

An aggregate is a plan javascript class. Here is an example of a MembershipFeesAggregate:

```typescript
class ShoppingCartItemEntity {
constructor(public readonly productId: string, public readonly quantity: number) {
}
}

class ShoppingCartAggregate {
constructor(
public readonly shoppingCartId: string,
public readonly maxItemsAllowed: number,
public readonly items: ShoppingCartItemEntity[] = [],
) {
}

static createSmallEmpty(shoppingCartId: string): ShoppingCartAggregate {
return new ShoppingCartAggregate(shoppingCartId, 5);
}

addItem(item: ShoppingCartItemEntity): void {
const totalQuantity = this.items.reduce((acc, currentItem) => acc + currentItem.quantity, 0);
if (totalQuantity + item.quantity > this.maxItemsAllowed) {
throw new Error(`Exceeded maximum items allowed in the shopping cart (${this.maxItemsAllowed})`);
}
this.items.push(item);
}

removeItem(productId: string): void {
const index = this.items.findIndex((item) => item.productId === productId);
if (index !== -1) this.items.splice(index, 1);
}
}
```

A few observations

- Use ubiquitous language
- Use static methods to create the aggregate
- Use different classes for entity or value object.

### Serializer

The aggregate is saved on a database. The serializer converts the aggregate to its db representation.

```typescript
import { ISerializer } from '@fizzbuds/ddd-toolkit';

// ShoppingCartAggregateModel can be a mongoose schema or a dynamodb table
type ShoppingCartAggregateModel = {
shoppingCartId: string;
maxItemsAllowed: number;
items: { productId: string; quantity: number }[];
};

export class ShoppingCartAggregateSerializer implements ISerializer<ShoppingCartAggregate, ShoppingCartAggregateModel> {
modelToAggregate(model: ShoppingCartAggregateModel): ShoppingCartAggregate {
return new ShoppingCartAggregate(
model.shoppingCartId,
model.maxItemsAllowed,
model.items.map(({ productId, quantity }) => new ShoppingCartItemEntity(productId, quantity)),
);
}

aggregateToModel(aggregate: ShoppingCartAggregate): ShoppingCartAggregateModel {
return {
shoppingCartId: aggregate.shoppingCartId,
maxItemsAllowed: aggregate.maxItemsAllowed,
items: aggregate.items.map(({ productId, quantity }) => ({ productId, quantity })),
};
}
}

```

### Mongo aggregate repo

```typescript
import { MongoAggregateRepo } from '@fizzbuds/ddd-toolkit';

export class ShoppingCartAggregateRepo extends MongoAggregateRepo<
ShoppingCartAggregate,
ShoppingCartAggregateModel
> {
private static logger = new Logger(ShoppingCartAggregateRepo.name);

constructor(mongoClient: MongoClient) {
super(
new ShoppingCartAggregateSerializer(),
mongoClient,
'shopping_carts',
undefined,
MemberRegistrationAggregateRepo.logger,
);
}
}
```

### Usage

```typescript
const mongoClient = await (new MongoClient('mongodb://localhost:27017')).connect()
const shoppingCartAggregateRepo = new ShoppingCartAggregateRepo(mongoClient);

const shoppingCart = ShoppingCartAggregate.createSmallEmpty('foo-cart-id');
await shoppingCartAggregateRepo.save(shoppingCart);

const retrievedShoppingCart = await shoppingCartAggregateRepo.getById('foo-cart-id');
retrievedShoppingCart.addItem(new ShoppingCartItemEntity('product-1', 1));
await shoppingCartAggregateRepo.save(retrievedShoppingCart);

```
Binary file added docs/assets/logo.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/logo.webp
Binary file not shown.
5 changes: 5 additions & 0 deletions docs/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Examples

Here are some examples of how to use the library.

- [Membership fees with NestJs](https://github.com/fizzbuds/nest-ddd-toolkit)
42 changes: 42 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Getting started

## Domain driven design

Domain-Driven Design (DDD) improves software by emphasizing understanding the problem domain and modeling it directly
within the software. This approach enhances communication, promotes modular design, and encourages iterative
development, leading to higher quality, more maintainable, and adaptable software systems.

## Motivation

For the inexperienced, building an application with DDD principles is not easy. The Web offers us many resources, and
navigating through them is not so simple.
We ran into complex implementations that created many problems for us and, above all, were not necessary for our
situation.

We decided to collect in this library the components of the tactical DDD for which we experienced a better cost-benefit
ratio.

We are well aware that the perfect solution does not exist, but these components provide an excellent starting point for
building an application that fully respects the principles of tactical DDD.

## Installation

::: warning
The idea is that the core package contains only the basic interfaces and implementations.

Additional packages will allow to install different implementations (repo with postgres, bus with rabbit etc etc)

At the current state however it contains the implementation for **mongodb**.
:::

```bash
pnpm install @fizzbuds/ddd-toolkit mongodb@5

```

At this time, the ddd-toolkit package offers the following features out of the box:

- **Aggregate repo** with _serialization_, _optimistic lock_, and optionally _outbox pattern_
- **Command bus** with in-memory implementation
- **Event bus** with in-memory implementation
- **Query bus** with in-memory implementation
30 changes: 30 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
# https://vitepress.dev/reference/default-theme-home-page
layout: home

hero:
name: "ddd-toolkit"
text: "Pragmatic approach to tactical DDD patterns"
image:
src: /assets/logo.jpeg
alt: logo
tagline: Well begun is half done 💎
actions:
- theme: brand
text: Getting Started
link: /getting-started
# - theme: alt
# text: API Docs
# link: /api-examples

features:
- title: 🧱 Aggregate
details: Ready to use aggregate repo with optimistic lock.
- title: 📥📤 Commands and Queries
details: Ready to use buses to standardize commands and queries.
- title: 🎉 Events
details: As the starting point of event storming
- title: 🪝 Repo hooks
details: The easiest way to separate reading from writing models.
---

8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@
"test:coverage": "pnpm run -r test:coverage",
"prepare": "husky install",
"check": "pnpm run -r check",
"ci:publish": "pnpm publish -r"
"ci:publish": "pnpm publish -r",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
},
"devDependencies": {
"@changesets/cli": "^2.27.1",
"husky": "^8.0.0",
"lint-staged": "^14.0.1"
"lint-staged": "^14.0.1",
"vitepress": "^1.1.3"
},
"lint-staged": {
"*.ts": "eslint --fix",
Expand Down
Loading

0 comments on commit 3dc67d1

Please sign in to comment.