Skip to content

Commit

Permalink
docs: updates
Browse files Browse the repository at this point in the history
  • Loading branch information
gearonix committed Oct 13, 2023
1 parent 85a593a commit 752d301
Show file tree
Hide file tree
Showing 13 changed files with 361 additions and 55 deletions.
73 changes: 59 additions & 14 deletions apps/docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -19,57 +19,102 @@ export default defineConfig({
{
text: 'Introduction',
items: [
{ base: 'introduction', text: 'About Project', link: '/about-project' },
{ base: 'introduction', text: 'Getting Started', link: '/getting-started' }
{
base: 'introduction',
text: 'About Project',
link: '/about-project'
},
{
base: 'introduction',
text: 'Getting Started',
link: '/getting-started'
}
]
},
{
text: 'Architecture',
items: [
{
base: 'architecture', text: 'Frontend', link: '/frontend'
base: 'architecture',
text: 'FSD and Microfrontends',
link: '/frontend'
},
{
base: 'architecture',
text: 'CQRS and Microservices',
link: '/backend'
}
]
},
{
text: 'Featutes',
text: 'Functionality',
items: [
{
base: 'features', text: 'Storybook', link: '/storybook'
base: 'functionality',
text: 'Prisma ORM',
link: '/prisma'
},
{
base: 'features', text: 'Code Generation', link: '/code-generation'
base: 'functionality',
text: 'GraphQL',
link: '/graphql'
}
]
},
{
text: 'Featutes',
items: [
{
base: 'features',
text: 'Storybook',
link: '/storybook'
},
{
base: 'features', text: 'Custom Libraries', link: '/custom-libraries'
base: 'features',
text: 'Code Generation',
link: '/code-generation'
},
{
base: 'features', text: 'Precommit Hooks', link: '/precommit-hooks'
base: 'features',
text: 'Custom Libraries',
link: '/custom-libraries'
},
{
base: 'features', text: 'Automation (CI/CD)', link: '/automation'
base: 'features',
text: 'Precommit Hooks',
link: '/precommit-hooks'
},
{
base: 'features', text: 'Docker', link: '/docker'
base: 'features',
text: 'Automation (CI/CD)',
link: '/automation'
},
{
base: 'features', text: 'Ts-Morph', link: '/ts-morph'
base: 'features',
text: 'Docker',
link: '/docker'
},
{
base: 'features', text: 'Config Incapsulation', link: '/config-incapsulation'
base: 'features',
text: 'Ts-Morph',
link: '/ts-morph'
},
{
base: 'features',
text: 'Config Incapsulation',
link: '/config-incapsulation'
}
]
}
],


socialLinks: [
{ icon: 'github', link: 'https://github.com/Gearonix/code-gear' }
],

footer: {
message: 'Released under the MIT License.',
copyright: 'Copyright © 2023-present Uzhanin Egor'
},
}
}
})
1 change: 1 addition & 0 deletions apps/docs/architecture/authorization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Authorization
132 changes: 132 additions & 0 deletions apps/docs/architecture/backend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Backend Architecture

At first, I stuck to the usual modular architecture. After that, I decided to switch to microservices.

*Microservices* are very well suited to the monorepository structure,
and I've also added the CQRS pattern that the [@nestjs/cqrs](https://docs.nestjs.com/recipes/cqrs) module provides.

## Kafka as a Message Broker

[Apache Kafka](https://kafka.apache.org/documentation/) is often used to solve the problems of messaging and transferring data between
different `microservices`. Unlike [RabbitMQ](https://www.rabbitmq.com/tutorials/tutorial-one-javascript.html), it focuses on high throughput and scalability.

I used Kafka + Zookeeper to implement communication between microservices.

::: code-group

```yaml [docker-compose.services.yml]
zookeeper:
image: confluentinc/cp-zookeeper:latest
container_name: zookeeper
networks:
- cgnet
env_file:
- .env
- .docker/.override.env
ports:
- 22181:2181

kafka:
image: confluentinc/cp-kafka:latest
container_name: kafka
hostname: kafka
networks:
- cgnet
depends_on:
- zookeeper
ports:
- 29092:29092
env_file:
- .env
- .docker/.override.env
```
:::tip NOTE
`.override.env` is a file to overwrite the local configuration for `docker`. It's important to add it

:::

## Usage with NestJS

You can use the module described below to *connect* the microservice.

```ts
@Module({
imports: [EnvModule],
providers: [KafkaService],
exports: [KafkaService]
})
/**
* Creates a reusable Kafka configuration
* @param service {Microservice} - name of your microservice
*/
export class KafkaModule {
static forRoot(service: Microservice): DynamicModule {
const providers: Provider[] = [
{
provide: service,
useFactory: (kafkaService: KafkaService) => {
return ClientProxyFactory.create(
kafkaService.getKafkaOptions(service)
)
},
inject: [KafkaService]
}
]
return {
module: KafkaModule,
providers,
exports: providers
}
}
}
```

Now, it can be connected in a single line.


```ts
@Module({
imports: [KafkaModule.forRoot(Microservice.CODE_EXECUTOR)]
})
/**
* You can now use it in the module.
*/
@Controller()
export class ExampleController {
constructor(
@Inject(Microservice.CODE_EXECUTOR) private executorClient: ClientKafka
) {}
async onModuleInit() {
this.executorClient.subscribeToResponseOf(CodeExecutorTopic.MY_TOPIC)
await this.executorClient.connect()
}
@Get()
async myRoute(
@Body() args: ExampleDTO
): Promise<unknown> {
return this.executorClient.send(CodeExecutorTopic.MY_TOPIC, args)
}
}
```


## CQRS Pattern

Using *CQRS* in a project is no different from the cqrs documentation of the NestJS module,
which can be found [here](https://docs.nestjs.com/recipes/cqrs).

You can find examples of usage in the microservice `apps/server/service-code-executor`.

## Gateway Pattern

I'm using the `gateway` pattern for microservices. In general, it is quite popular, you can read more
[here](https://microservices.io/patterns/apigateway.html).

74 changes: 74 additions & 0 deletions apps/docs/functionality/graphql.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# GraphQL

In the project, I used *GraphQL* for client-server communication.

[GraphQL](https://graphql.org/learn/) - is a query language, architectural style, and toolset for creating and managing APIs.

## Schema Generation

I used the [@nestjs/graphql](https://docs.nestjs.com/graphql/quick-start#installation) package to autogenerate the
*GraphQL schema* from typescript interfaces
([Code First](https://docs.nestjs.com/graphql/quick-start#code-first)).


::: warning

There may be problems with webpack hot reload during autogeneration,
in which case check that the `_schema.gql` file is in `.nxignore`.

::: code-group
```sh [.nxignore]
_schema.gql
```
You can read more about [.nxignore](https://nx.dev/reference/nxignore) here.

:::

Here's a common use case with `@nestjs/graphql`.

```ts
// API gateway

@Resolver(() => Example)
export class ExampleResolver {
constructor(private authService: AuthService) {}

@Mutation(() => Example)
@UseGuards(GqlAuthGuard, GqlLocalAuthGuard)
async signIn(
@Args(graphqlArg) payload: SignIn,
@WithUser() user: User
): Promise<AccessToken> {
return this.authService.generateToken(user.username)
}
}


```

## Using the `@grnx-utils/apollo` package.

On the frontend, you can use the [@grnx-utils/apollo](https://www.npmjs.com/package/@grnx-utils/apollo) package to
reduce the number of boilerplate code.

```ts
import { createApolloClient } from '@grnx-utils/apollo'

const apollo = createApolloClient({
url: graphqlEndpoint
})

class AuthServices implements IAuthServices {
async getProfile() {
const [payload, error] = await apollo.request<Request, Response>(
exampleQuery
)

if (payload) {
this.state.username = payload.username
this.state.isAuthorized = true
}
}
}

```
54 changes: 54 additions & 0 deletions apps/docs/functionality/prisma.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Prisma ORM

I've been using [Prisma](https://www.prisma.io/docs/getting-started/quickstart)
as an [ORM](https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping) and in this module I'll cover some interesting concepts I've found.

## Auto-generation of prisma types

To avoid duplicating types, you can generate prisma types and then reuse them between the frontend and backend.

::: code-group

```sh [yarn]
nx prisma:types gateway
```

:::

Under the hood, I use the [@kalissaac/prisma-typegen](https://www.npmjs.com/package/@kalissaac/prisma-typegen) module that allows me to do this.

```ts
// AUTO GENERATED FILE BY @kalissaac/prisma-typegen
// DO NOT EDIT

export interface Example {
name: string
}
```

## Using multiple Prisma files

In the project, you can group and create `.prisma` files wherever you want, in the end,
this script will merge all the .prisma files into one `schema.prisma`

```js
// ~/gateway/prisma/concat-prisma-files.js

export const concatPrismaFiles = ({ config, models, dest }) => {
concat(
[
resolvePrisma('config', config),
...models.map((model) => resolvePrisma('models', `${model}.prisma`))
],
resolvePrisma(dest),
(error) => {
if (error) {
throw error
}

console.log('Prisma files merged.')
}
)
}

```
2 changes: 1 addition & 1 deletion apps/server/gateway/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"dependsOn": ["prisma:concat"]
},
"prisma:types": {
"command": "yarn dlx @kalissaac/prisma-typegen ./../../../packages/nest-common/src/types/_prisma.ts ./prisma/schema.prisma",
"command": "yarn dlx @kalissaac/prisma-typegen ./../../../packages/api/common/src/types/_prisma.ts ./prisma/schema.prisma",
"options": {
"cwd": "apps/server/gateway"
},
Expand Down
2 changes: 1 addition & 1 deletion apps/server/service-code-executor/.config/jest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import { createJestConfig } from 'cg-config/src/jest'

export default createJestConfig({
displayName: 'service-code-executor',
layer: 'second',
layer: 'second'
})
Loading

0 comments on commit 752d301

Please sign in to comment.