archstone 1.0.2 → 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 (2) hide show
  1. package/README.md +103 -92
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,15 +1,22 @@
1
+ <div align="center">
2
+
1
3
  # archstone
2
4
 
3
- [![npm version](https://img.shields.io/npm/v/archstone?style=flat-square)](https://www.npmjs.com/package/archstone)
4
- [![license: MIT](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](./LICENSE)
5
- [![TypeScript](https://img.shields.io/badge/TypeScript-5-blue?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
6
- [![Bun](https://img.shields.io/badge/Bun-runtime-black?style=flat-square&logo=bun)](https://bun.sh)
5
+ **TypeScript architecture foundation for backend services.**
6
+ Stop re-implementing DDD boilerplate. Focus on your domain.
7
+
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)
12
+
13
+ </div>
7
14
 
8
- > TypeScript architecture foundation for backend services based on Domain-Driven Design (DDD) and Clean Architecture.
15
+ ---
9
16
 
10
- 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.
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.
11
18
 
12
- ## Installation
19
+ ## Install
13
20
 
14
21
  ```bash
15
22
  bun add archstone
@@ -17,57 +24,24 @@ bun add archstone
17
24
  npm install archstone
18
25
  ```
19
26
 
20
- ## Packages
27
+ ## At a Glance
21
28
 
22
- | Import path | Contents |
29
+ | Building block | What it does |
23
30
  |---|---|
24
- | `archstone/core` | `Either`, `ValueObject`, `UniqueEntityId`, `WatchedList`, `Optional` |
25
- | `archstone/domain` | All domain exports |
26
- | `archstone/domain/enterprise` | `Entity`, `AggregateRoot`, `DomainEvent`, `DomainEvents`, `EventHandler` |
27
- | `archstone/domain/application` | `UseCase`, `UseCaseError`, repository contracts |
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 |
28
38
 
29
- ## Architecture
39
+ ## Usage
30
40
 
31
- ```
32
- src/
33
- ├── core/ # Language-level utilities with no domain knowledge
34
- │ ├── either.ts # Functional error handling (Left / Right)
35
- │ ├── value-object.ts # Base class for value objects
36
- │ ├── unique-entity-id.ts # UUID v7 identity wrapper
37
- │ ├── watched-list.ts # Change-tracked collection for aggregates
38
- │ └── types/
39
- │ └── optional.ts # Optional<T, K> utility type
40
-
41
- └── domain/
42
- ├── enterprise/ # Pure domain model — no framework deps
43
- │ ├── entities/
44
- │ │ ├── entity.ts # Identity-based base entity
45
- │ │ └── aggregate-root.ts # Event-raising aggregate base
46
- │ └── events/
47
- │ ├── domain-event.ts # DomainEvent interface
48
- │ ├── domain-events.ts # Singleton registry & dispatcher
49
- │ └── event-handler.ts # EventHandler interface
50
-
51
- └── application/ # Orchestration — use cases & repository contracts
52
- ├── use-cases/
53
- │ ├── use-case.ts # UseCase<Input, Output> interface
54
- │ └── use-case.error.ts # Base error type for use case failures
55
- └── repositories/
56
- ├── repository.ts # Full CRUD contract (composed)
57
- ├── findabe.ts # Findable<T>
58
- ├── creatable.ts # Creatable<T>
59
- ├── saveble.ts # Saveable<T>
60
- └── deletable.ts # Deletable<T>
61
- ```
62
-
63
- ## Core Concepts
64
-
65
- ### Either — functional error handling
66
-
67
- Use cases never throw. They return an `Either<Error, Value>` — left for failure, right for success.
41
+ ### Either — handle errors without throwing
68
42
 
69
43
  ```ts
70
- import { Either, left, right } from "archstone/core"
44
+ import { Either, left, right } from 'archstone/core'
71
45
 
72
46
  type Result = Either<UserNotFoundError, User>
73
47
 
@@ -77,18 +51,20 @@ async function findUser(id: string): Promise<Result> {
77
51
  return right(user)
78
52
  }
79
53
 
80
- const result = await findUser("123")
81
- if (result.isLeft()) console.error(result.value) // UserNotFoundError
82
- else console.log(result.value) // User
83
- ```
54
+ const result = await findUser('123')
84
55
 
85
- ### Entity & AggregateRoot
56
+ if (result.isLeft()) {
57
+ console.error(result.value) // UserNotFoundError
58
+ } else {
59
+ console.log(result.value) // User
60
+ }
61
+ ```
86
62
 
87
- Entities are defined by identity. Aggregates extend that with domain event support.
63
+ ### Entity & AggregateRoot model your domain
88
64
 
89
65
  ```ts
90
- import { AggregateRoot } from "archstone/domain/enterprise"
91
- import { UniqueEntityId, Optional } from "archstone/core"
66
+ import { AggregateRoot } from 'archstone/domain/enterprise'
67
+ import { UniqueEntityId, Optional } from 'archstone/core'
92
68
 
93
69
  interface OrderProps {
94
70
  customerId: UniqueEntityId
@@ -98,24 +74,23 @@ interface OrderProps {
98
74
 
99
75
  class Order extends AggregateRoot<OrderProps> {
100
76
  get customerId() { return this.props.customerId }
101
- get total() { return this.props.total }
77
+ get total() { return this.props.total }
102
78
 
103
- static create(props: Optional<OrderProps, "createdAt">): Order {
104
- const order = new Order(
105
- { ...props, createdAt: props.createdAt ?? new Date() },
106
- )
79
+ static create(props: Optional<OrderProps, 'createdAt'>): Order {
80
+ const order = new Order({
81
+ ...props,
82
+ createdAt: props.createdAt ?? new Date(),
83
+ })
107
84
  order.addDomainEvent(new OrderCreatedEvent(order))
108
85
  return order
109
86
  }
110
87
  }
111
88
  ```
112
89
 
113
- ### ValueObject
114
-
115
- Value objects are equal by their properties, not by reference.
90
+ ### ValueObject — equality by value
116
91
 
117
92
  ```ts
118
- import { ValueObject } from "archstone/core"
93
+ import { ValueObject } from 'archstone/core'
119
94
 
120
95
  interface EmailProps { value: string }
121
96
 
@@ -123,18 +98,20 @@ class Email extends ValueObject<EmailProps> {
123
98
  get value() { return this.props.value }
124
99
 
125
100
  static create(raw: string): Email {
126
- if (!raw.includes("@")) throw new Error("Invalid email")
101
+ if (!raw.includes('@')) throw new Error('Invalid email')
127
102
  return new Email({ value: raw.toLowerCase() })
128
103
  }
129
104
  }
130
- ```
131
105
 
132
- ### WatchedList
106
+ const a = Email.create('user@example.com')
107
+ const b = Email.create('user@example.com')
108
+ a.equals(b) // true
109
+ ```
133
110
 
134
- Track additions and removals in a collection without rewriting the whole thing on save.
111
+ ### WatchedList track collection changes
135
112
 
136
113
  ```ts
137
- import { WatchedList } from "archstone/core"
114
+ import { WatchedList } from 'archstone/core'
138
115
 
139
116
  class TagList extends WatchedList<Tag> {
140
117
  compareItems(a: Tag, b: Tag) { return a.id.equals(b.id) }
@@ -148,48 +125,82 @@ tags.getNewItems() // [newTag]
148
125
  tags.getRemovedItems() // [existingTag]
149
126
  ```
150
127
 
151
- ### Domain Events
152
-
153
- Events are raised inside aggregates and dispatched by the infrastructure layer after successful persistence.
128
+ ### Domain Events — decouple side effects
154
129
 
155
130
  ```ts
156
- import { DomainEvents } from "archstone/domain/enterprise"
131
+ import { DomainEvents } from 'archstone/domain/enterprise'
157
132
 
158
- // register a handler
133
+ // Register a handler
159
134
  DomainEvents.register(
160
135
  (event) => sendWelcomeEmail(event as UserCreatedEvent),
161
136
  UserCreatedEvent.name,
162
137
  )
163
138
 
164
- // infrastructure dispatches after persisting
139
+ // Dispatch after persisting the aggregate
165
140
  await userRepository.create(user)
166
141
  DomainEvents.dispatchEventsForAggregate(user.id)
167
142
  ```
168
143
 
169
- ### Repository Contracts
170
-
171
- Repositories are defined as interfaces in the application layer. Implementations live in infrastructure.
144
+ ### Repository Contracts — keep infra out of your domain
172
145
 
173
146
  ```ts
174
- import { Repository, Creatable } from "archstone/domain/application"
147
+ import { Repository, Creatable } from 'archstone/domain/application'
175
148
 
176
- // application/repositories/user-repository.ts
149
+ // Compose the interface you need
177
150
  export interface UserRepository extends Repository<User> {
178
151
  findByEmail(email: string): Promise<User | null>
179
152
  }
180
153
 
181
- // or compose only what you need
154
+ // Or only what you need
182
155
  export interface AuditRepository extends Creatable<AuditLog> {}
183
156
  ```
184
157
 
185
- ## Contributing
158
+ ## Package Exports
186
159
 
187
- Contributions are welcome! Please read [CONTRIBUTING.md](./CONTRIBUTING.md) first.
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
198
+ ```
188
199
 
189
- ## Code of Conduct
200
+ ---
190
201
 
191
- This project follows the [Contributor Covenant](./CODE_OF_CONDUCT.md).
202
+ <div align="center">
192
203
 
193
- ## License
204
+ [Contributing](./CONTRIBUTING.md) · [Code of Conduct](./CODE_OF_CONDUCT.md) · [MIT License](./LICENSE)
194
205
 
195
- MIT — see [LICENSE](./LICENSE).
206
+ </div>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "archstone",
3
3
  "description": "TypeScript architecture foundation for backend services based on Domain-Driven Design and Clean Architecture",
4
- "version": "1.0.2",
4
+ "version": "1.0.3",
5
5
  "type": "module",
6
6
  "private": false,
7
7
  "files": [