archstone 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +112 -79
  3. package/package.json +29 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 João Henrique Benatti Coimbra
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,51 +1,47 @@
1
+ <div align="center">
2
+
1
3
  # archstone
2
4
 
3
- A TypeScript-first architecture foundation for backend services, built around Domain-Driven Design (DDD) and Clean Architecture principles.
5
+ **TypeScript architecture foundation for backend services.**
6
+ Stop re-implementing DDD boilerplate. Focus on your domain.
4
7
 
5
- Archstone provides the core building blocks — entities, value objects, aggregates, domain events, use cases, and repository contracts — so you can focus on your domain logic instead of re-implementing the same structural patterns across every project.
8
+ [![npm](https://img.shields.io/npm/v/archstone?style=flat-square&color=black)](https://www.npmjs.com/package/archstone)
9
+ [![license](https://img.shields.io/badge/license-MIT-black?style=flat-square)](./LICENSE)
10
+ [![typescript](https://img.shields.io/badge/TypeScript-5-black?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
11
+ [![bun](https://img.shields.io/badge/Bun-runtime-black?style=flat-square&logo=bun)](https://bun.sh)
6
12
 
7
- ## Layers
13
+ </div>
8
14
 
15
+ ---
16
+
17
+ Archstone gives you the structural pieces of **Domain-Driven Design** and **Clean Architecture** — entities, value objects, aggregates, domain events, use cases, and repository contracts — so every project starts from a solid, consistent foundation.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ bun add archstone
23
+ # or
24
+ npm install archstone
9
25
  ```
10
- src/
11
- ├── core/ # Language-level utilities with no domain knowledge
12
- │ ├── either.ts # Functional error handling (Left / Right)
13
- │ ├── value-object.ts # Base class for value objects
14
- │ ├── unique-entity-id.ts # UUID v7 identity wrapper
15
- │ ├── watched-list.ts # Change-tracked collection for aggregates
16
- │ └── types/
17
- │ └── optional.ts # Optional<T, K> utility type
18
-
19
- └── domain/
20
- ├── enterprise/ # Pure domain model — no framework deps
21
- │ ├── entities/
22
- │ │ ├── entity.ts # Identity-based base entity
23
- │ │ └── aggregate-root.ts # Event-raising aggregate base
24
- │ └── events/
25
- │ ├── domain-event.ts # DomainEvent interface
26
- │ ├── domain-events.ts # Singleton registry & dispatcher
27
- │ └── event-handler.ts # EventHandler interface
28
-
29
- └── application/ # Orchestration — use cases & repository contracts
30
- ├── use-cases/
31
- │ ├── use-case.ts # UseCase<Input, Output> interface
32
- │ └── use-case.error.ts # Base error type for use case failures
33
- └── repositories/
34
- ├── repository.ts # Full CRUD contract (composed)
35
- ├── findabe.ts # Findable<T>
36
- ├── creatable.ts # Creatable<T>
37
- ├── saveble.ts # Saveable<T>
38
- └── deletable.ts # Deletable<T>
39
- ```
40
26
 
41
- ## Core Concepts
27
+ ## At a Glance
28
+
29
+ | Building block | What it does |
30
+ |---|---|
31
+ | `Entity` / `AggregateRoot` | Identity-based domain objects; aggregates raise domain events |
32
+ | `ValueObject` | Equality by value, not reference |
33
+ | `UniqueEntityId` | UUID v7 identity wrapper |
34
+ | `WatchedList` | Tracks additions and removals in a collection |
35
+ | `Either` | Functional error handling — no throwing in use cases |
36
+ | `UseCase` | Contract for application use cases returning `Either` |
37
+ | `Repository` | CRUD interface contracts — implementations live in infra |
42
38
 
43
- ### Either — functional error handling
39
+ ## Usage
44
40
 
45
- Use cases never throw. They return an `Either<Error, Value>` left for failure, right for success.
41
+ ### Either — handle errors without throwing
46
42
 
47
43
  ```ts
48
- import { Either, left, right } from "@/core/either"
44
+ import { Either, left, right } from 'archstone/core'
49
45
 
50
46
  type Result = Either<UserNotFoundError, User>
51
47
 
@@ -55,19 +51,20 @@ async function findUser(id: string): Promise<Result> {
55
51
  return right(user)
56
52
  }
57
53
 
58
- const result = await findUser("123")
59
- if (result.isLeft()) console.error(result.value) // UserNotFoundError
60
- else console.log(result.value) // User
61
- ```
54
+ const result = await findUser('123')
62
55
 
63
- ### Entity & AggregateRoot
56
+ if (result.isLeft()) {
57
+ console.error(result.value) // UserNotFoundError
58
+ } else {
59
+ console.log(result.value) // User
60
+ }
61
+ ```
64
62
 
65
- Entities are defined by identity. Aggregates extend that with domain event support.
63
+ ### Entity & AggregateRoot model your domain
66
64
 
67
65
  ```ts
68
- import { AggregateRoot } from "@/domain/enterprise/entities/aggregate-root"
69
- import { UniqueEntityId } from "@/core/unique-entity-id"
70
- import { Optional } from "@/core/types/optional"
66
+ import { AggregateRoot } from 'archstone/domain/enterprise'
67
+ import { UniqueEntityId, Optional } from 'archstone/core'
71
68
 
72
69
  interface OrderProps {
73
70
  customerId: UniqueEntityId
@@ -77,24 +74,23 @@ interface OrderProps {
77
74
 
78
75
  class Order extends AggregateRoot<OrderProps> {
79
76
  get customerId() { return this.props.customerId }
80
- get total() { return this.props.total }
77
+ get total() { return this.props.total }
81
78
 
82
- static create(props: Optional<OrderProps, "createdAt">): Order {
83
- const order = new Order(
84
- { ...props, createdAt: props.createdAt ?? new Date() },
85
- )
79
+ static create(props: Optional<OrderProps, 'createdAt'>): Order {
80
+ const order = new Order({
81
+ ...props,
82
+ createdAt: props.createdAt ?? new Date(),
83
+ })
86
84
  order.addDomainEvent(new OrderCreatedEvent(order))
87
85
  return order
88
86
  }
89
87
  }
90
88
  ```
91
89
 
92
- ### ValueObject
93
-
94
- Value objects are equal by their properties, not by reference.
90
+ ### ValueObject — equality by value
95
91
 
96
92
  ```ts
97
- import { ValueObject } from "@/core/value-object"
93
+ import { ValueObject } from 'archstone/core'
98
94
 
99
95
  interface EmailProps { value: string }
100
96
 
@@ -102,17 +98,21 @@ class Email extends ValueObject<EmailProps> {
102
98
  get value() { return this.props.value }
103
99
 
104
100
  static create(raw: string): Email {
105
- if (!raw.includes("@")) throw new Error("Invalid email")
101
+ if (!raw.includes('@')) throw new Error('Invalid email')
106
102
  return new Email({ value: raw.toLowerCase() })
107
103
  }
108
104
  }
109
- ```
110
105
 
111
- ### WatchedList
106
+ const a = Email.create('user@example.com')
107
+ const b = Email.create('user@example.com')
108
+ a.equals(b) // true
109
+ ```
112
110
 
113
- Track additions and removals in a collection without rewriting the whole thing on save.
111
+ ### WatchedList track collection changes
114
112
 
115
113
  ```ts
114
+ import { WatchedList } from 'archstone/core'
115
+
116
116
  class TagList extends WatchedList<Tag> {
117
117
  compareItems(a: Tag, b: Tag) { return a.id.equals(b.id) }
118
118
  }
@@ -125,49 +125,82 @@ tags.getNewItems() // [newTag]
125
125
  tags.getRemovedItems() // [existingTag]
126
126
  ```
127
127
 
128
- ### Domain Events
129
-
130
- Events are raised inside aggregates and dispatched by the infrastructure layer after successful persistence.
128
+ ### Domain Events — decouple side effects
131
129
 
132
130
  ```ts
133
- // register a handler
131
+ import { DomainEvents } from 'archstone/domain/enterprise'
132
+
133
+ // Register a handler
134
134
  DomainEvents.register(
135
135
  (event) => sendWelcomeEmail(event as UserCreatedEvent),
136
136
  UserCreatedEvent.name,
137
137
  )
138
138
 
139
- // infrastructure dispatches after persisting
139
+ // Dispatch after persisting the aggregate
140
140
  await userRepository.create(user)
141
141
  DomainEvents.dispatchEventsForAggregate(user.id)
142
142
  ```
143
143
 
144
- ### Repository Contracts
145
-
146
- Repositories are defined as interfaces in the application layer. Implementations live in infrastructure.
144
+ ### Repository Contracts — keep infra out of your domain
147
145
 
148
146
  ```ts
149
- // application/repositories/user-repository.ts
147
+ import { Repository, Creatable } from 'archstone/domain/application'
148
+
149
+ // Compose the interface you need
150
150
  export interface UserRepository extends Repository<User> {
151
151
  findByEmail(email: string): Promise<User | null>
152
152
  }
153
153
 
154
- // or compose only what you need
154
+ // Or only what you need
155
155
  export interface AuditRepository extends Creatable<AuditLog> {}
156
156
  ```
157
157
 
158
- ## Getting Started
158
+ ## Package Exports
159
159
 
160
- ```bash
161
- bun install
162
- bun test
160
+ ```
161
+ archstone/core → Either, ValueObject, UniqueEntityId, WatchedList, Optional
162
+ archstone/domain → all domain exports
163
+ archstone/domain/enterprise → Entity, AggregateRoot, DomainEvent, DomainEvents, EventHandler
164
+ archstone/domain/application → UseCase, UseCaseError, repository contracts
165
+ ```
166
+
167
+ ## Layer Architecture
168
+
169
+ ```
170
+ src/
171
+ ├── core/ # Zero domain knowledge — pure utilities
172
+ │ ├── either.ts
173
+ │ ├── value-object.ts
174
+ │ ├── unique-entity-id.ts
175
+ │ ├── watched-list.ts
176
+ │ └── types/optional.ts
177
+
178
+ └── domain/
179
+ ├── enterprise/ # Pure domain model — no framework deps
180
+ │ ├── entities/
181
+ │ │ ├── entity.ts
182
+ │ │ └── aggregate-root.ts
183
+ │ └── events/
184
+ │ ├── domain-event.ts
185
+ │ ├── domain-events.ts
186
+ │ └── event-handler.ts
187
+
188
+ └── application/ # Use cases & repository contracts
189
+ ├── use-cases/
190
+ │ ├── use-case.ts
191
+ │ └── use-case.error.ts
192
+ └── repositories/
193
+ ├── repository.ts
194
+ ├── findable.ts
195
+ ├── creatable.ts
196
+ ├── saveable.ts
197
+ └── deletable.ts
163
198
  ```
164
199
 
165
- ## Tech
200
+ ---
166
201
 
167
- - [Bun](https://bun.sh) — runtime, bundler, and test runner
168
- - TypeScript 5 with strict mode
169
- - Zero runtime dependencies
202
+ <div align="center">
170
203
 
171
- ## License
204
+ [Contributing](./CONTRIBUTING.md) · [Code of Conduct](./CODE_OF_CONDUCT.md) · [MIT License](./LICENSE)
172
205
 
173
- MIT
206
+ </div>
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "archstone",
3
- "version": "1.0.1",
3
+ "description": "TypeScript architecture foundation for backend services based on Domain-Driven Design and Clean Architecture",
4
+ "version": "1.0.3",
4
5
  "type": "module",
5
6
  "private": false,
6
7
  "files": [
@@ -41,6 +42,33 @@
41
42
  },
42
43
  "./package.json": "./package.json"
43
44
  },
45
+ "keywords": [
46
+ "ddd",
47
+ "domain-driven-design",
48
+ "clean-architecture",
49
+ "typescript",
50
+ "entity",
51
+ "aggregate",
52
+ "value-object",
53
+ "either",
54
+ "domain-events",
55
+ "repository",
56
+ "use-case",
57
+ "bun"
58
+ ],
59
+ "author": {
60
+ "name": "João Henrique Benatti Coimbra",
61
+ "url": "https://github.com/joao-coimbra"
62
+ },
63
+ "license": "MIT",
64
+ "homepage": "https://github.com/joao-coimbra/archstone#readme",
65
+ "repository": {
66
+ "type": "git",
67
+ "url": "git+https://github.com/joao-coimbra/archstone.git"
68
+ },
69
+ "bugs": {
70
+ "url": "https://github.com/joao-coimbra/archstone/issues"
71
+ },
44
72
  "scripts": {
45
73
  "build": "bunup",
46
74
  "dev": "bunup --watch",