@magek/mcp-server 0.0.8

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 (52) hide show
  1. package/README.md +42 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +25 -0
  4. package/dist/prompts/cqrs-flow.d.ts +15 -0
  5. package/dist/prompts/cqrs-flow.js +252 -0
  6. package/dist/prompts/troubleshooting.d.ts +15 -0
  7. package/dist/prompts/troubleshooting.js +239 -0
  8. package/dist/resources/cli-reference.d.ts +13 -0
  9. package/dist/resources/cli-reference.js +193 -0
  10. package/dist/resources/documentation.d.ts +18 -0
  11. package/dist/resources/documentation.js +62 -0
  12. package/dist/server.d.ts +5 -0
  13. package/dist/server.js +127 -0
  14. package/dist/utils/docs-loader.d.ts +19 -0
  15. package/dist/utils/docs-loader.js +111 -0
  16. package/docs/advanced/custom-templates.md +96 -0
  17. package/docs/advanced/data-migrations.md +181 -0
  18. package/docs/advanced/environment-configuration.md +74 -0
  19. package/docs/advanced/framework-packages.md +17 -0
  20. package/docs/advanced/health/sensor-health.md +389 -0
  21. package/docs/advanced/instrumentation.md +135 -0
  22. package/docs/advanced/register.md +119 -0
  23. package/docs/advanced/sensor.md +10 -0
  24. package/docs/advanced/testing.md +96 -0
  25. package/docs/advanced/touch-entities.md +45 -0
  26. package/docs/architecture/command.md +367 -0
  27. package/docs/architecture/entity.md +214 -0
  28. package/docs/architecture/event-driven.md +30 -0
  29. package/docs/architecture/event-handler.md +108 -0
  30. package/docs/architecture/event.md +145 -0
  31. package/docs/architecture/notifications.md +54 -0
  32. package/docs/architecture/queries.md +207 -0
  33. package/docs/architecture/read-model.md +507 -0
  34. package/docs/contributing.md +349 -0
  35. package/docs/docs-index.json +200 -0
  36. package/docs/features/error-handling.md +204 -0
  37. package/docs/features/event-stream.md +35 -0
  38. package/docs/features/logging.md +81 -0
  39. package/docs/features/schedule-actions.md +44 -0
  40. package/docs/getting-started/ai-coding-assistants.md +181 -0
  41. package/docs/getting-started/coding.md +543 -0
  42. package/docs/getting-started/installation.md +143 -0
  43. package/docs/graphql.md +1213 -0
  44. package/docs/index.md +62 -0
  45. package/docs/introduction.md +58 -0
  46. package/docs/magek-arch.png +0 -0
  47. package/docs/magek-cli.md +67 -0
  48. package/docs/magek-logo.svg +1 -0
  49. package/docs/security/authentication.md +189 -0
  50. package/docs/security/authorization.md +242 -0
  51. package/docs/security/security.md +16 -0
  52. package/package.json +46 -0
@@ -0,0 +1,367 @@
1
+ ---
2
+ title: "Commands"
3
+ group: "Architecture"
4
+ ---
5
+
6
+ # Command
7
+
8
+ Commands are any action a user performs on your application. For example, `RemoveItemFromCart`, `RatePhoto` or `AddCommentToPost`. They express the intention of an user, and they are the main interaction mechanism of your application. They are a similar to the concept of a **request on a REST API**. Command issuers can also send data on a command as parameters.
9
+
10
+ ## Creating a command
11
+
12
+ The Magek CLI will help you to create new commands. You just need to run the following command and the CLI will generate all the boilerplate for you:
13
+
14
+ ```bash
15
+ npx magek new:command CreateProduct --fields sku:SKU displayName:string description:string price:Money
16
+ ```
17
+
18
+ This will generate a new file called `create-product` in the `src/commands` directory. You can also create the file manually, but you will need to create the class and decorate it, so we recommend using the CLI.
19
+
20
+ ## Declaring a command
21
+
22
+ In Magek you define them as TypeScript classes decorated with the `@Command` decorator. The `Command` parameters will be declared as properties of the class.
23
+
24
+ ```typescript title="src/commands/command-name.ts"
25
+ @Command({
26
+ authorize: 'all',
27
+ })
28
+ export class CommandName {
29
+ @field()
30
+ readonly fieldA!: SomeType
31
+
32
+ @field()
33
+ readonly fieldB!: SomeOtherType
34
+ }
35
+ ```
36
+
37
+ These commands are handled by `Command Handlers`, the same way a **REST Controller** do with a request. To create a `Command handler` of a specific Command, you must declare a `handle` class function inside the corresponding command you want to handle. For example:
38
+
39
+ ```typescript title="src/commands/command-name.ts"
40
+ @Command({
41
+ authorize: 'all',
42
+ })
43
+ export class CommandName {
44
+ @field()
45
+ readonly fieldA!: SomeType
46
+
47
+ @field()
48
+ readonly fieldB!: SomeOtherType
49
+
50
+ // highlight-start
51
+ public static async handle(command: CommandName, register: Register): Promise<void> {
52
+ // Validate inputs
53
+ // Run domain logic
54
+ // register.events([event1,...])
55
+ }
56
+ // highlight-end
57
+ }
58
+ ```
59
+
60
+ Magek will then generate the GraphQL mutation for the corresponding command, and the infrastructure to handle them. You only have to define the class and the handler function. Commands are part of the public API, so you can define authorization policies for them, you can read more about this on [the authorization section](/security/authorization).
61
+
62
+ > **Tip:** We recommend using command handlers to validate input data before registering events into the event store because they are immutable once there.
63
+
64
+ ## The command handler function
65
+
66
+ Each command class must have a method called `handle`. This function is the command handler, and it will be called by the framework every time one instance of this command is submitted. Inside the handler you can run validations, return errors, query entities to make decisions, and register relevant domain events.
67
+
68
+ ### Registering events
69
+
70
+ Within the command handler execution, it is possible to register domain events. The command handler function receives the `register` argument, so within the handler, it is possible to call `register.events(...)` with a list of events.
71
+
72
+ ```typescript title="src/commands/create-product.ts"
73
+ @Command({
74
+ authorize: 'all',
75
+ })
76
+ export class CreateProduct {
77
+ @field()
78
+ readonly sku!: string
79
+
80
+ @field()
81
+ readonly price!: number
82
+
83
+ public static async handle(command: CreateProduct, register: Register): Promise<void> {
84
+ // highlight-next-line
85
+ register.event(new ProductCreated(/*...*/))
86
+ }
87
+ }
88
+ ```
89
+
90
+ For more details about events and the register parameter, see the [`Events`](/architecture/event) section.
91
+
92
+ ### Returning a value
93
+
94
+ The command handler function can return a value. This value will be the response of the GraphQL mutation. By default, the command handler function expects you to return a `void` as a return type. Since GrahpQL does not have a `void` type, the command handler function returns `true` when called through the GraphQL. This is because the GraphQL specification requires a response, and `true` is the most appropriate value to represent a successful execution with no return value.
95
+
96
+ If you want to return a value, you need to:
97
+ 1. Change the return type of the handler function
98
+ 2. Use the `@returns` decorator to specify the GraphQL return type
99
+
100
+ The `@returns` decorator tells Magek what type to use in the generated GraphQL schema. Without it, Magek defaults to `Boolean` for the mutation return type.
101
+
102
+ For example, if you want to return a `string`:
103
+
104
+ ```typescript title="src/commands/create-product.ts"
105
+ @Command({
106
+ authorize: 'all',
107
+ })
108
+ export class CreateProduct {
109
+ @field()
110
+ readonly sku!: string
111
+
112
+ @field()
113
+ readonly price!: number
114
+
115
+ // highlight-next-line
116
+ @returns(type => String)
117
+ public static async handle(command: CreateProduct, register: Register): Promise<string> {
118
+ register.event(new ProductCreated(/*...*/))
119
+ // highlight-next-line
120
+ return 'Product created!'
121
+ }
122
+ }
123
+ ```
124
+
125
+ #### The @returns decorator
126
+
127
+ The `@returns` decorator is required when your command handler returns a value other than `void`. It uses the same type function pattern as `@field()`:
128
+
129
+ ```typescript
130
+ // Return a primitive type
131
+ @returns(type => String)
132
+ public static async handle(...): Promise<string> { ... }
133
+
134
+ // Return a UUID (maps to GraphQL ID)
135
+ @returns(type => UUID)
136
+ public static async handle(...): Promise<UUID> { ... }
137
+
138
+ // Return a number
139
+ @returns(type => Number)
140
+ public static async handle(...): Promise<number> { ... }
141
+
142
+ // Return an array
143
+ @returns(type => [CartItem])
144
+ public static async handle(...): Promise<CartItem[]> { ... }
145
+ ```
146
+
147
+ > **Note:** The type specified in `@returns` is the GraphQL return type, not the TypeScript type. TypeScript erases type information at compile time, so Magek cannot infer the return type automatically.
148
+
149
+ ### Validating data
150
+
151
+ > **Tip:** Magek uses the typed nature of GraphQL to ensure that types are correct before reaching the handler, so **you don't have to validate types**.
152
+
153
+ #### Throw an error
154
+
155
+ A command will fail if there is an uncaught error during its handling. When a command fails, Magek will return a detailed error response with the message of the thrown error. This is useful for debugging, but it is also a security feature. Magek will never return an error stack trace to the client, so you don't have to worry about exposing internal implementation details.
156
+
157
+ One case where you might want to throw an error is when the command is invalid because it breaks a business rule. For example, if the command contains a negative price. In that case, you can throw an error in the handler. Magek will use the error's message as the response to make it descriptive. For example, given this command:
158
+
159
+ ```typescript title="src/commands/create-product.ts"
160
+ @Command({
161
+ authorize: 'all',
162
+ })
163
+ export class CreateProduct {
164
+ @field()
165
+ readonly sku!: string
166
+
167
+ @field()
168
+ readonly price!: number
169
+
170
+ public static async handle(command: CreateProduct, register: Register): Promise<void> {
171
+ const priceLimit = 10
172
+ if (command.price >= priceLimit) {
173
+ // highlight-next-line
174
+ throw new Error(`price must be below ${priceLimit}, and it was ${command.price}`)
175
+ }
176
+ }
177
+ }
178
+ ```
179
+
180
+ You'll get something like this response:
181
+
182
+ ```json
183
+ {
184
+ "errors": [
185
+ {
186
+ "message": "price must be below 10, and it was 19.99",
187
+ "path": ["CreateProduct"]
188
+ }
189
+ ]
190
+ }
191
+ ```
192
+
193
+ #### Register error events
194
+
195
+ There could be situations in which you want to register an event representing an error. For example, when moving items with insufficient stock from one location to another:
196
+
197
+ ```typescript title="src/commands/move-stock.ts"
198
+ @Command({
199
+ authorize: 'all',
200
+ })
201
+ export class MoveStock {
202
+ @field()
203
+ readonly productID!: string
204
+
205
+ @field()
206
+ readonly origin!: string
207
+
208
+ @field()
209
+ readonly destination!: string
210
+
211
+ @field()
212
+ readonly quantity!: number
213
+
214
+ public static async handle(command: MoveStock, register: Register): Promise<void> {
215
+ if (!command.enoughStock(command.productID, command.origin, command.quantity)) {
216
+ // highlight-next-line
217
+ register.events(new ErrorEvent(`There is not enough stock for ${command.productID} at ${command.origin}`))
218
+ } else {
219
+ register.events(new StockMoved(/*...*/))
220
+ }
221
+ }
222
+
223
+ private enoughStock(productID: string, origin: string, quantity: number): boolean {
224
+ /* ... */
225
+ }
226
+ }
227
+ ```
228
+
229
+ In this case, the command operation can still be completed. An event handler will take care of that `ErrorEvent and proceed accordingly.
230
+
231
+ ### Reading entities
232
+
233
+ Event handlers are a good place to make decisions and, to make better decisions, you need information. The `Magek.entity` function allows you to inspect the application state. This function receives two arguments, the `Entity`'s name to fetch and the `entityID`. Here is an example of fetching an entity called `Stock`:
234
+
235
+ ```typescript title="src/commands/move-stock.ts"
236
+ @Command({
237
+ authorize: 'all',
238
+ })
239
+ export class MoveStock {
240
+ @field()
241
+ readonly productID!: string
242
+
243
+ @field()
244
+ readonly origin!: string
245
+
246
+ @field()
247
+ readonly destination!: string
248
+
249
+ @field()
250
+ readonly quantity!: number
251
+
252
+ public static async handle(command: MoveStock, register: Register): Promise<void> {
253
+ // highlight-next-line
254
+ const stock = await Magek.entity(Stock, command.productID)
255
+ if (!command.enoughStock(command.origin, command.quantity, stock)) {
256
+ register.events(new ErrorEvent(`There is not enough stock for ${command.productID} at ${command.origin}`))
257
+ }
258
+ }
259
+
260
+ private enoughStock(origin: string, quantity: number, stock?: Stock): boolean {
261
+ const count = stock?.countByLocation[origin]
262
+ return !!count && count >= quantity
263
+ }
264
+ }
265
+ ```
266
+
267
+ ## Authorizing a command
268
+
269
+ Commands are part of the public API of a Magek application, so you can define who is authorized to submit them. All commands are protected by default, which means that no one can submit them. In order to allow users to submit a command, you must explicitly authorize them. You can use the `authorize` field of the `@Command` decorator to specify the authorization rule.
270
+
271
+ ```typescript title="src/commands/create-product.ts"
272
+ @Command({
273
+ // highlight-next-line
274
+ authorize: 'all',
275
+ })
276
+ export class CreateProduct {
277
+ @field()
278
+ readonly sku!: Sku
279
+
280
+ @field()
281
+ readonly displayName!: string
282
+
283
+ @field()
284
+ readonly description!: string
285
+
286
+ @field()
287
+ readonly price!: number
288
+
289
+ public static async handle(command: CreateProduct, register: Register): Promise<void> {
290
+ register.events(/* YOUR EVENT HERE */)
291
+ }
292
+ }
293
+ ```
294
+
295
+ You can read more about this on the [Authorization section](/security/authorization).
296
+
297
+ ## Submitting a command
298
+
299
+ Magek commands are accessible to the outside world as GraphQL mutations. GrahpQL fits very well with Magek's CQRS approach because it has two kinds of operations: Mutations and Queries. Mutations are actions that modify the server-side data, just like commands.
300
+
301
+ Magek automatically creates one mutation per command. The framework infers the mutation input type from the command fields. Given this `CreateProduct` command:
302
+
303
+ ```typescript
304
+ @Command({
305
+ authorize: 'all',
306
+ })
307
+ export class CreateProduct {
308
+ @field()
309
+ readonly sku!: Sku
310
+
311
+ @field()
312
+ readonly displayName!: string
313
+
314
+ @field()
315
+ readonly description!: string
316
+
317
+ @field()
318
+ readonly price!: number
319
+
320
+ public static async handle(command: CreateProduct, register: Register): Promise<void> {
321
+ register.events(/* YOUR EVENT HERE */)
322
+ }
323
+ }
324
+ ```
325
+
326
+ Magek generates the following GraphQL mutation:
327
+
328
+ ```graphql
329
+ mutation CreateProduct($input: CreateProductInput!): Boolean
330
+ ```
331
+
332
+ where the schema for `CreateProductInput` is
333
+
334
+ ```text
335
+ {
336
+ sku: String
337
+ displayName: String
338
+ description: String
339
+ price: Float
340
+ }
341
+ ```
342
+
343
+ ## Commands naming convention
344
+
345
+ Semantics are very important in Magek as it will play an essential role in designing a coherent system. Your application should reflect your domain concepts, and commands are not an exception. Although you can name commands in any way you want, we strongly recommend you to **name them starting with verbs in imperative plus the object being affected**. If we were designing an e-commerce application, some commands would be:
346
+
347
+ - CreateProduct
348
+ - DeleteProduct
349
+ - UpdateProduct
350
+ - ChangeCartItems
351
+ - ConfirmPayment
352
+ - MoveStock
353
+ - UpdateCartShippingAddress
354
+
355
+ Despite you can place commands, and other Magek files, in any directory, we strongly recommend you to put them in `<project-root>/src/commands`. Having all the commands in one place will help you to understand your application's capabilities at a glance.
356
+
357
+ ```text
358
+ <project-root>
359
+ ├── src
360
+ │ ├── commands <------ put them here
361
+ │ ├── common
362
+ │ ├── config
363
+ │ ├── entities
364
+ │ ├── events
365
+ │ ├── index.ts
366
+ │ └── read-models
367
+ ```
@@ -0,0 +1,214 @@
1
+ ---
2
+ title: "Entities"
3
+ group: "Architecture"
4
+ ---
5
+
6
+ # Entity
7
+
8
+ If events are the _source of truth_ of your application, entities are the _current state_ of your application. For example, if you have an application that allows users to create bank accounts, the events would be something like `AccountCreated`, `MoneyDeposited`, `MoneyWithdrawn`, etc. But the entities would be the `BankAccount` themselves, with the current balance, owner, etc.
9
+
10
+ Entities are created by _reducing_ the whole event stream. Magek generates entities on the fly, so you don't have to worry about their creation. However, you must define them in order to instruct Magek how to generate them.
11
+
12
+ > **Info:** Under the hood, Magek stores snapshots of the entities in order to reduce the load on the event store. That way, Magek doesn't have to reduce the whole event stream whenever the current state of an entity is needed.
13
+
14
+ ## Creating entities
15
+
16
+ The Magek CLI will help you to create new entities. You just need to run the following command and the CLI will generate all the boilerplate for you:
17
+
18
+ ```bash
19
+ npx magek new:entity Product --fields displayName:string description:string price:Money
20
+ ```
21
+
22
+ This will generate a new file called `product.ts` in the `src/entities` directory. You can also create the file manually, but you will need to create the class and decorate it, so we recommend using the CLI.
23
+
24
+ ## Declaring an entity
25
+
26
+ To declare an entity in Magek, you must define a class decorated with the `@Entity` decorator. Inside of the class, you must define a constructor with all the fields you want to have in your entity.
27
+
28
+ ```typescript title="src/entities/entity-name.ts"
29
+ @Entity
30
+ export class EntityName {
31
+ @field(type => UUID)
32
+ public id!: UUID
33
+
34
+ @field()
35
+ readonly fieldA!: SomeType
36
+
37
+ @field()
38
+ readonly fieldB!: SomeOtherType
39
+ }
40
+ ```
41
+
42
+ ## The reduce function
43
+
44
+ In order to tell Magek how to reduce the events, you must define a static method decorated with the `@reduces` decorator. This method will be called by the framework every time an event of the specified type is emitted. The reducer method must return a new entity instance with the current state of the entity.
45
+
46
+ ```typescript title="src/entities/entity-name.ts"
47
+ @Entity
48
+ export class EntityName {
49
+ @field(type => UUID)
50
+ public id!: UUID
51
+
52
+ @field()
53
+ readonly fieldA!: SomeType
54
+
55
+ @field()
56
+ readonly fieldB!: SomeOtherType
57
+
58
+ // highlight-start
59
+ @reduces(SomeEvent)
60
+ public static reduceSomeEvent(event: SomeEvent, currentEntityState?: EntityName): EntityName {
61
+ return evolve(currentEntityState, {
62
+ id: event.entityID(),
63
+ fieldA: event.fieldA,
64
+ fieldB: event.fieldB,
65
+ })
66
+ }
67
+ // highlight-end
68
+ }
69
+ ```
70
+
71
+ The reducer method receives two parameters:
72
+
73
+ - `event` - The event object that triggered the reducer
74
+ - `currentEntity?` - The current state of the entity instance that the event belongs to if it exists. **This parameter is optional** and will be `undefined` if the entity doesn't exist yet (For example, when you process a `ProductCreated` event that will generate the first version of a `Product` entity).
75
+
76
+ > **Tip:** The `evolve()` helper function creates a new immutable copy of the entity with the specified field updates. It safely handles the case where `currentEntityState` is `undefined` (for newly created entities) and ensures immutability by returning a fresh object rather than mutating the existing one.
77
+
78
+ ### Reducing multiple events
79
+
80
+ You can define as many reducer methods as you want, each one for a different event type. For example, if you have a `Cart` entity, you could define a reducer for `ProductAdded` events and another one for `ProductRemoved` events.
81
+
82
+ ```typescript title="src/entities/cart.ts"
83
+ @Entity
84
+ export class Cart {
85
+ @field(type => UUID)
86
+ public id!: UUID
87
+
88
+ @field()
89
+ readonly items!: Array<CartItem>
90
+
91
+ @reduces(ProductAdded)
92
+ public static reduceProductAdded(event: ProductAdded, currentCart?: Cart): Cart {
93
+ const newItems = addToCart(event.item, currentCart)
94
+ return evolve(currentCart, { id: event.entityID(), items: newItems })
95
+ }
96
+
97
+ @reduces(ProductRemoved)
98
+ public static reduceProductRemoved(event: ProductRemoved, currentCart?: Cart): Cart {
99
+ const newItems = removeFromCart(event.item, currentCart)
100
+ return evolve(currentCart, { items: newItems })
101
+ }
102
+ }
103
+ ```
104
+
105
+ > **Tip:** It's highly recommended to **keep your reducer functions pure**, which means that you should be able to produce the new entity version by just looking at the event and the current entity state. You should avoid calling third party services, reading or writing to a database, or changing any external state.
106
+
107
+ ### Skipping Events with ReducerAction.Skip
108
+
109
+ Sometimes a reducer may need to skip an event instead of updating the entity state. This can happen when:
110
+ - Receiving an update event for an entity that doesn't exist
111
+ - Processing an event that is no longer relevant
112
+ - Handling events that should be ignored under certain conditions
113
+
114
+ To skip an event, return `ReducerAction.Skip` from your reducer. This tells Magek to keep the current entity state unchanged:
115
+
116
+ ```typescript title="src/entities/product.ts"
117
+ import { ReducerAction, ReducerResult } from '@magek/common'
118
+
119
+ @Entity
120
+ export class Product {
121
+ @field(type => UUID)
122
+ public id!: UUID
123
+
124
+ @field()
125
+ readonly name!: string
126
+
127
+ @field()
128
+ readonly price!: number
129
+
130
+ @reduces(ProductUpdated)
131
+ public static reduceProductUpdated(
132
+ event: ProductUpdated,
133
+ currentProduct?: Product
134
+ ): ReducerResult<Product> {
135
+ if (!currentProduct) {
136
+ // Can't update a non-existent product - skip this event
137
+ return ReducerAction.Skip
138
+ }
139
+ return new Product(currentProduct.id, event.newName, event.newPrice)
140
+ }
141
+ }
142
+ ```
143
+
144
+ When a reducer returns `ReducerAction.Skip`, the framework will:
145
+ - Keep the previous entity snapshot unchanged
146
+ - Continue processing subsequent events in the event stream
147
+ - Not store a new snapshot for this event
148
+
149
+ > **Note:** `ReducerAction.Skip` should be used sparingly and only for events that genuinely should not affect entity state. In most cases, properly designed events and reducers won't need to skip events.
150
+
151
+ There could be a lot of events being reduced concurrently among many entities, but, **for a specific entity instance, the events order is preserved**. This means that while one event is being reduced, all other events of any kind _that belong to the same entity instance_ will be waiting in a queue until the previous reducer has finished. This is how Magek guarantees that the entity state is consistent.
152
+
153
+ ![reducer process gif](/img/reducer.gif)
154
+
155
+ ### Eventual Consistency
156
+
157
+ Additionally, due to the event driven and async nature of Magek, your data might not be instantly updated. Magek will consume the commands, generate events, and _eventually_ generate the entities. Most of the time this is not perceivable, but under huge loads, it could be noticed.
158
+
159
+ This property is called [Eventual Consistency](https://en.wikipedia.org/wiki/Eventual_consistency), and it is a trade-off to have high availability for extreme situations, where other systems might simply fail.
160
+
161
+ ## Entity ID
162
+
163
+ In order to identify each entity instance, you must define an `id` field on each entity. This field will be used by the framework to identify the entity instance. If the value of the `id` field matches the value returned by the [`entityID()` method](./event.md#events-and-entities) of an Event, the framework will consider that the event belongs to that entity instance.
164
+
165
+ ```typescript title="src/entities/entity-name.ts"
166
+ @Entity
167
+ export class EntityName {
168
+ // highlight-next-line
169
+ @field(type => UUID)
170
+ public id!: UUID
171
+
172
+ @field()
173
+ readonly fieldA!: SomeType
174
+
175
+ @field()
176
+ readonly fieldB!: SomeOtherType
177
+
178
+ @reduces(SomeEvent)
179
+ public static reduceSomeEvent(event: SomeEvent, currentEntityState?: EntityName): EntityName {
180
+ return evolve(currentEntityState, {
181
+ id: event.entityID(),
182
+ fieldA: event.fieldA,
183
+ })
184
+ }
185
+ }
186
+ ```
187
+
188
+ > **Tip:** We recommend you to use the `UUID` type for the `id` field. You can generate a new `UUID` value by calling the `UUID.generate()` method already provided by the framework.
189
+
190
+ ## Entities naming convention
191
+
192
+ Entities are a representation of your application state in a specific moment, so name them as closely to your domain objects as possible. Typical entity names are nouns that might appear when you think about your app. In an e-commerce application, some entities would be:
193
+
194
+ - Cart
195
+ - Product
196
+ - UserProfile
197
+ - Order
198
+ - Address
199
+ - PaymentMethod
200
+ - Stock
201
+
202
+ Entities live within the entities directory of the project source: `<project-root>/src/entities`.
203
+
204
+ ```text
205
+ <project-root>
206
+ ├── src
207
+ │ ├── commands
208
+ │ ├── common
209
+ │ ├── config
210
+ │ ├── entities <------ put them here
211
+ │ ├── events
212
+ │ ├── index.ts
213
+ │ └── read-models
214
+ ```
@@ -0,0 +1,30 @@
1
+ ---
2
+ title: "Event-Driven Architecture"
3
+ group: "Architecture"
4
+ ---
5
+
6
+ # Magek architecture
7
+
8
+ Magek is a highly opinionated framework that provides a complete toolset to build production-ready event-driven serverless applications.
9
+
10
+ Two patterns influence the Magek's event-driven architecture: Command-Query Responsibility Segregation ([CQRS](https://www.martinfowler.com/bliki/CQRS.html)) and [Event Sourcing](https://martinfowler.com/eaaDev/EventSourcing.html). They're complex techniques to implement from scratch with lower-level frameworks, but Magek makes them feel natural and very easy to use.
11
+
12
+ ![architecture](../magek-arch.png)
13
+
14
+ As you can see in the diagram, Magek applications consist of four main building blocks: `Commands`, `Events`, `Entities`, and `Read Models`. `Commands` and `Read Models` are the public interface of the application, while `Events` and `Entities` are private implementation details. With Magek, clients submit `Commands`, query the `Read Models`, or subscribe to them for receiving real-time updates thanks to the out of the box [GraphQL API](/graphql)
15
+
16
+ Magek applications are event-driven and event-sourced so, **the source of truth is the whole history of events**. When a client submits a command, Magek _wakes up_ and handles it throght `Command Handlers`. As part of the process, some `Events` may be _registered_ as needed.
17
+
18
+ On the other side, the framework caches the current state by automatically _reducing_ all the registered events into `Entities`. You can also _react_ to events via `Event Handlers`, triggering side effect actions to certain events. Finally, `Entities` are not directly exposed, they are transformed or _projected_ into `ReadModels`, which are exposed to the public.
19
+
20
+ In this chapter you'll walk through these concepts in detail.
21
+
22
+ ## Architecture Components
23
+
24
+ - [Commands](./command.md) - The input interface for your application
25
+ - [Events](./event.md) - Records of facts that represent the source of truth
26
+ - [Event Handlers](./event-handler.md) - React to events and trigger side effects
27
+ - [Entities](./entity.md) - Current state derived from events
28
+ - [Read Models](./read-model.md) - Public projections of your data
29
+ - [Notifications](./notifications.md) - Real-time updates and messaging
30
+ - [Queries](./queries.md) - How to query your read models