Skip to content

Commit 0cb95a3

Browse files
author
Braden Wong
committed
docs: show multiple service patterns with tabs
Instead of prescribing one pattern over another, this change presents all three approaches for grouping service functions (plain object, namespace, abstract class) using tabs, letting developers choose based on their background and preferences. Changes: - Add Tab component to best-practice.md for service pattern examples - Update main service.ts example to use plain object (simplest approach) - Keep all three patterns as valid options in the Service section - Revert controller examples to original (abstract class) - Remove Function Containers section from typescript.md (rationale belongs in PR)
1 parent 5e5da8b commit 0cb95a3

File tree

1 file changed

+72
-9
lines changed

1 file changed

+72
-9
lines changed

docs/essential/best-practice.md

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ head:
1414
content: Elysia is a pattern agnostic framework, we leave the decision up to you and your team for coding patterns to use. However, we found that there are several who are using MVC pattern (Model-View-Controller) on Elysia, and found it's hard to decouple and handle types. This page is a guide to use Elysia with MVC pattern.
1515
---
1616

17+
<script setup>
18+
import Tab from '../components/fern/tab.vue'
19+
</script>
20+
1721
# Best Practice
1822

1923
Elysia is a pattern-agnostic framework, leaving the decision of which coding patterns to use up to you and your team.
@@ -85,10 +89,9 @@ import { status } from 'elysia'
8589

8690
import type { AuthModel } from './model'
8791

88-
// If the class doesn't need to store a property,
89-
// you may use `abstract class` to avoid class allocation
90-
export abstract class Auth {
91-
static async signIn({ username, password }: AuthModel.signInBody) {
92+
// Group related functions - see Service section for alternative patterns
93+
export const Auth = {
94+
async signIn({ username, password }: AuthModel.signInBody) {
9295
const user = await sql`
9396
SELECT password
9497
FROM users
@@ -280,16 +283,72 @@ There are 2 types of service in Elysia:
280283

281284
We recommend abstracting a service class/function away from Elysia.
282285

283-
If the service or function isn't tied to an HTTP request or doesn't access a `Context`, it's recommended to implement it as a static class or function.
286+
If the service or function isn't tied to an HTTP request or doesn't access a `Context`, you can group related functions using several patterns depending on your preference:
287+
288+
<Tab
289+
id="service-pattern"
290+
:names="['Plain Object', 'Namespace', 'Abstract Class']"
291+
:tabs="['object', 'namespace', 'class']"
292+
>
293+
294+
<template v-slot:object>
295+
296+
The simplest approach is using a plain object. This is idiomatic JavaScript and easy to understand:
297+
298+
```typescript
299+
import { Elysia, t } from 'elysia'
300+
301+
const Service = {
302+
fibo(number: number): number {
303+
if (number < 2) return number
304+
return Service.fibo(number - 1) + Service.fibo(number - 2)
305+
}
306+
}
307+
308+
new Elysia()
309+
.get('/fibo', ({ body }) => {
310+
return Service.fibo(body)
311+
}, {
312+
body: t.Numeric()
313+
})
314+
```
315+
316+
</template>
317+
318+
<template v-slot:namespace>
319+
320+
TypeScript namespaces can hold both values and types with the same name, which is useful if you want to co-locate types with your service:
321+
322+
```typescript
323+
import { Elysia, t } from 'elysia'
324+
325+
namespace Service {
326+
export function fibo(number: number): number {
327+
if (number < 2) return number
328+
return Service.fibo(number - 1) + Service.fibo(number - 2)
329+
}
330+
}
331+
332+
new Elysia()
333+
.get('/fibo', ({ body }) => {
334+
return Service.fibo(body)
335+
}, {
336+
body: t.Numeric()
337+
})
338+
```
339+
340+
</template>
341+
342+
<template v-slot:class>
343+
344+
If you're coming from Java or C#, you may prefer using an abstract class with static methods:
284345

285346
```typescript
286347
import { Elysia, t } from 'elysia'
287348

288349
abstract class Service {
289350
static fibo(number: number): number {
290-
if(number < 2)
291-
return number
292-
351+
if (number < 2) return number
293352
return Service.fibo(number - 1) + Service.fibo(number - 2)
294353
}
295354
}
@@ -302,7 +361,11 @@ new Elysia()
302361
})
303362
```
304363

305-
If your service doesn't need to store a property, you may use `abstract class` and `static` instead to avoid allocating class instance.
364+
</template>
365+
366+
</Tab>
367+
368+
All three patterns produce the same runtime behavior. Choose based on your team's familiarity and whether you need to co-locate types.
306369

307370
### 2. Request dependent service as Elysia instance
308371

0 commit comments

Comments
 (0)