Skip to content

Commit

Permalink
Improve READMEs
Browse files Browse the repository at this point in the history
  • Loading branch information
Nikita Pavlovskiy committed Aug 20, 2023
1 parent 09fe26f commit 627de57
Show file tree
Hide file tree
Showing 22 changed files with 274 additions and 25 deletions.
34 changes: 31 additions & 3 deletions .k8s/local/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
# A word of warning
# Launching the project locally


## 1. A command to initialize minikube cluster

```bash
minikube start --mount-string="$(pwd):/minikube-host" --mount --addons=ingress,coredns
```

Execute in the root of the project. (pwd = repo root)

## 2. DNS hack

There's an issue in Nextauth library. It currently does not support separate backend and frontend SSO urls:
https://github.com/nextauthjs/next-auth/issues/2637
Expand Down Expand Up @@ -49,7 +60,24 @@ metadata:

This line enables website app to access the SSO over the external address.

## 3. /etc/hosts records

# A command to initialize minikube cluster
Put the following lines to the bottom of `/etc/hosts` file:

```hosts
127.0.0.1 host.minikube.internal
127.0.0.1 carres.local
127.0.0.1 api.carres.local
127.0.0.1 sso.carres.local
127.0.0.1 rmq.carres.local
```

This would enable the browser to access the whole ecosystem locally.

## 4. Apply Kubernetes configs:

```bash
kubectl apply -f .k8s/local/carres-\*.yaml
```

minikube start --mount-string="$(pwd [or any other sort to specify this repository's root directory]):/minikube-host" --mount --addons=ingress,coredns
It takes about 1 minute for the project to start due to Keycloak initialization.
62 changes: 62 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# A word about the architectures in general

## Goals of every architecture

Every architecture servers to several primary goals:

- make the maintenance cost lower (by reducing the complexity)
- keep the project from being blocked by technologies and decisions
- induce programmers to follow a certain idea and don't step too far from the Right Path ;)

Or, speaking practically, you have to put your files in folders, otherwise you are soon to be overwhelmed by chaos. Not good. You need some line to go along.

## The evolution of architectures

This long list of architectures and their core features is here to let you know how the bits of SOLID acronym have been discovered. And why currently Clean Architecture accumulates the best from every period.

> _(let's not mention the "procedural" "architectures" that precede 1980s as those don't really lead to any complexity reduction)_
> _"CA reference!" remark would mean that please remember this idea, as it will be important to comprehend the finishing nature of Clean Architecture.)_
Architectures have being evolving for half a century already, and from the topmost perspective the path looks like this:

- [>> MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) (1979)
- The whole application logic is handled procedurally in Controllers.
- Models just mirror the state of the database providing the access to one.
- [>> MVVM](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel) (and MV-\*\*) (2000s)
- Applications grow bigger, so does a typical view. Template engines provide the way to handle the conditional logic, but even so the templates tend to bloat out.
- Here the View Model concept comes, simplifying the data flow back to the normal level.
- [>> Three Layer Architecture](https://medium.com/@deanrubin/the-three-layered-architecture-fe30cb0e4a6) (2002)
- Presentation -> Business Logic -> DBAL
- DBAL becomes separated from the business logic, and business logic knows nothing about the presentation layer.
- Clean separate layers with some relative freedom of tactical patterns to use, within the layer. _**CA reference! [[OCP](https://www.digitalocean.com/community/conceptual-articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design#open-closed-principle)]**_
- [>> DDD](https://betterprogramming.pub/why-domain-driven-design-203099adf32a) (2003; the article by the link is not so short, but so is the whole concept)
- Today, DDD is more of a strategy of the concerns separation: business modules interact as separate but friendly units, no responsibility leakage and so on. But at the beginning, DDD used to impose a certain architectural approach.
- The core concept: the Business is the thing everything else in the system submits to. _**CA reference! [[DIP](https://www.digitalocean.com/community/conceptual-articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design#dependency-inversion-principle)]**_
- Bound Contexts as the isolated units of the whole large business. Sometimes, even as smaller semi-independent micro-applications. System consists of Bound Contexts. Each Bound Context can use whatever architecture it is, with the only restriction to always be able to communicate with other Contexts in a discussed way.
- Here DBAL becomes LESS than a layer. _**CA reference! [[LSP](https://www.digitalocean.com/community/conceptual-articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design#liskov-substitution-principle)]**_ It, for the first time, becomes only a piece of infrastructure: **"We don't care of how the data is stored. The modelling of the processes is the key."**
- [>> Ports and Adapters](https://medium.com/idealo-tech-blog/hexagonal-ports-adapters-architecture-e3617bcf00a0) (2005)
- While DDD focuses on isolating the holy business rules and processes from the dirty implementation substance, Ports and Adapters architecture brings the separation _frequently changing_ and quite _constant_ application parts. _**CA reference! [[SRP](https://www.digitalocean.com/community/conceptual-articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design#single-responsibility-principle)]**_
- Now, it's officially okay to have multiple input streams for the same application, as now it's easy to leverage the input due to isolated **Ports**, and so it's easy to switch from one storage to another quickly, due to easily replaceable **Adapters**.
- The Domain part becomes prior to DBAL. DBAL now lies somewhere out of the heart of the system, mirroring the Domain objects to enable these for the persistence.
- [>> Onion Architecture](https://www.codeguru.com/csharp/understanding-onion-architecture/) (2008)
- It's an extension of Ports and Adapters architecture, in the sense of better separation of the entities into layers.
- It's now three separate layers: Domain, Application, Infrastructure. Infrastructure framework-wise replaces some of the Application interfaces to be used Usecase Handlers. _**CA reference! [[ISP](https://www.digitalocean.com/community/conceptual-articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design#interface-segregation-principle)]**_ (Please know the difference from [>> Three Layer Architecture](https://www.codeguru.com/csharp/understanding-onion-architecture/)!)
- A clean, unbroken single direction of the dependencies flow: from the outside to the inside. Layer violation is now really a _violation_. It's called **Dependency Inversion Principle**, as what "D" stands for in Solid.
- [>> Clean Architecture](https://betterprogramming.pub/the-clean-architecture-beginners-guide-e4b7058c1165) (2012)
- [>> This](https://dev.to/eminetto/clean-architecture-2-years-later-4een) is also a good grasp on how CA looks down on the ground.
- _**CA reference**_: Takes the best from some of the preceding architectures. If you put together everything highlighted in the previous records, you'll result in **SOLID** principle, combined from multiple sources, discovered in the experimental fashion. Clean Architecture implements the pure vision of the easily maintainable code.
- Generally, four layers: Domain, Application, Presentation, Infrastructure. Strict dependency direction (out -> in). Easy way to keep code being changed for not more than one reason.
- Freedom not to make decisions until it's necessary: you can always create an Application-level interface with some ultra-simplistic implementation in Infrastructure to postpone the limitations imposed by any certain decision. **Each decision is a loss of other options.**
- [>> This article](https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/) by [@hgraca](https://herbertograca.com) puts together all the half-a-century knowledge, making a bit too complicated but still declarative scheme of all the elements of this architecture. Make sure to read it. It proves the idea of Clean Architecture being something of the last complex grasp on the architecture, combining the best from various preceding approaches.

> This is a textual version of a brilliant lecture by [>> Fyodor Schyudlo](https://www.facebook.com/people/%D0%A4%D0%B5%D0%B4%D0%BE%D1%80-%D0%A9%D1%83%D0%B4%D0%BB%D0%BE/100011119055934/) at DotNetRu conference, with some additions from my side. See the [>> full version](https://www.youtube.com/watch?v=WXelYPjwmk0) or the [>> slides](https://speakerdeck.com/dotnetru/fiedor-shchudlo-evoliutsiia-enterprise-arkhitiektur-ot-mvc-do-clean-architecture). **Unfortunately, russian language only,** but you can use the automatic subtitle generation feature, if possible.
> Materials by Herberto Graca aka [>> @hraca](https://herbertograca.com) are extensively used also.
## The usage of Clean Architecture

During my carrer, I try to use Clean Architecture at every place where it makes sense for the following reasons:

- it's easier to hand over a product organized with the clear idea in mind. Clean Architecture is definitely such an idea. A lot of references and a ton of credit.
- it reduces the cost of the in-house maintenance, since across all the projects we use the similar ideas. Less effort, less errors, less precious cognitive load for the same amount of use.
21 changes: 21 additions & 0 deletions MONITORING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Cars Reservation System - Monitoring & Dashboards

Grafana allows to visualize various metrics in the most beautiful way.

### Here are examples of dashboard data:

## Valuable for business

<img src="apps/website/public/images/Screenshot 2023-08-20 at 02.37.22.png" alt="Business metrics">

## JVM indicators

<img src="apps/website/public/images/Screenshot 2023-08-20 at 02.41.05.png" alt="JVM indicators">

## Kubernetes cluster

<img src="apps/website/public/images/Screenshot 2023-08-20 at 02.42.13.png" alt="Kubernetes cluster metrics">

## Next.js (NodeJS) application

<img src="apps/website/public/images/Screenshot 2023-08-20 at 02.45.19.png" alt="Next.js app metrics">
97 changes: 83 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,104 @@
# Cars reservation
# [Cars Reservation System](https://cars-reservation.nikitades.com)

This is a test project. It's created to put together all the knowledge I have about creating web apps with Spring (and eventually also Javascript and some DevOps practiques).
<p align="center">
<a href="https://cars-reservation.nikitades.com">
<img width=60% src="apps/website/public/images/Screenshot 2023-08-20 at 02.07.27.png">
</a>
</p>

## Applications and parts
![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/leathermen/carres/master_merge.yaml)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=leathermen_carres_api&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=leathermen_carres_api) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=leathermen_carres_api&metric=coverage)](https://sonarcloud.io/summary/new_code?id=leathermen_carres_api) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=leathermen_carres_api&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=leathermen_carres_api)

- [Spring Boot API (readme)](apps/backend/api/README.md)
- [React (Next.js) frontend app (readme)](apps/website/README.md)
- [Keycloak SSO server (readme)](apps/keycloak/README.md)
- [CloudFlare reverse proxying (readme)](REVERSE-PROXYING.md)
</p>

## Ideas
### A test project to put together some knowledge I have about creating web apps with Spring (and also eventually Javascript and some DevOps practiques).

This whole project is expected to visualize the idea of the web application. The API is covered with tests thoroughly, both in unit and integration style. The domain area of the app is rather simple, so it takes no Ubiquitous Language to describe. Also, DDD techniques were intentionally not used to speed up the development and also out of lack of reason to use ones: the project is small, as I already said.
#### Website: [https://cars-reservation.nikitades.com](https://cars-reservation.nikitades.com)

#### My LinkedIn: [https://www.linkedin.com/in/nikita-pavlovskiy-6b3618127/](https://www.linkedin.com/in/nikita-pavlovskiy-6b3618127/)

The project is hosted at Hetzner, using Kubernetes setup.
The Continuous Integration is done using GitHub actions.

DDOS mitigation is achieved by hiding the real IP address of the cluster entrypoints, by reverse proxying all the requests through CloudFlare system. It's known for being able to withstand attacks of a solid numbers of requests.
*Promoted demo admin account (with the access to the Dashboard):*
```
login: [email protected]
password: example15
```

## Remarkable elements (please take a look at those)
**Please, log in with Github or Google or register yourself with registration form.**


## Applications and parts

<table>
<tr>
<td width="60%">
<ul>
<li>Spring Boot API (<a href="apps/backend/api/README.md">readme</a>)</li>
<li>Spring Boot Telegram Notifier (<a href="apps/backend/tg-notifier/README.md">readme</a>)</li>
<li>React (Next.js) frontend app (<a href="apps/website/README.md">readme</a>)</li>
<li>Keycloak SSO server (<a href="apps/keycloak/README.md">readme</a>)</li>
<li>CloudFlare reverse proxying (<a href="REVERSE-PROXYING.md">readme</a>)</li>
</ul>
</td>
<td>
<img width=100% src="apps/website/public/images/cars-reservation-logo.png">
<td>
</tr>
</table>

## Ideas

This whole project is supposed to visualize the idea of a great web application. The API is covered with tests thoroughly, both in unit and integration style. The domain area of the app is rather simple, so it takes no Ubiquitous Language to describe. Also, DDD techniques were intentionally not used to speed up the development and also out of lack of reason to use ones: the project is small, as I already said.

## Visual metrics and materials

- [API Swagger UI](https://cars-reservation-api.nikitades.com/open/swagger-ui)
- [**Public Sonarcloud report**](https://sonarcloud.io/summary/new_code?id=leathermen_carres_api) (the coverage included)
- [TODO public Grafana dashboard of technical and business metrics](somewhere)

## General technical specification

The project is hosted at Hetzner (ARM), using Kubernetes setup.
The Continuous Integration is done using GitHub actions.

DDOS mitigation is achieved by hiding the real IP address of the cluster entrypoints, by reverse proxying all the requests through CloudFlare system. It's known for being able to withstand attacks of a solid density.

Github Actions is used as CI/CD platform. An extensive set prefabricated actions helps a lot.

Please see more in root folders of every particular application.

## Technologies used

- Spring Boot (main API & Telegram notifier)
- Next.js (SSR website)
- Keycloak (SSO service)
- Rabbit MQ (AMQP bus)
- Kubernetes (orchestration)
- Prometheus (metrics scraping)p
- Grafana (metrics visualization)
- CloudFlare (reverse proxying and DDOS mitigation)
- SonarCloud (code quality gate)

## Metrics & observability

Prometheus is responsible for scraping metrics from services. The observed services:

- Spring Boot API
- Next.js website
- Rabbit MQ
- Kubernetes cluster itself

Typically metrics endpoints are protected with OAuth2 security. Only users of "Cars Reservation" realm with role "metrics-scraper" are allowed to the metrics data. Prometheus is capable of obtaining the JWT agains the SSO service on its own.

## Local launch

This project is supposed to be launched locally with Minikube or any other sort of local K8S cluster.
You will need [Minikube](https://minikube.sigs.k8s.io/docs/start/) or similar technology to achieve this.

See more at ["Launching locally"](.k8s/local/README.md).

## Credits

The author: Nikita Pavlovskiy, [[email protected]]([email protected]).

Contact me in Telegram: [@nikitades](https://t.me/nikitades).
Contact me in Telegram: [@nikitades](https://t.me/nikitades).
18 changes: 17 additions & 1 deletion apps/backend/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,20 @@ I decided to go the most straightforward way: to put all the encapsulated logic

## Stack & ideas

It's Spring 3.0.2 and Hibernate. Users are authenticated by provided JWT. The auth provider is the Keycloak server. The session is stateless. The app is built with the idea of distributed apps in mind. So each app's replica is replaceable and can handle requests in parallel with other ones. The 12-factor app idea was considered at the time of the creation of this application.
It's Spring 3.0.2 and Hibernate. Users are authenticated by provided JWT. The auth provider is the Keycloak server. The session is stateless. The app is built with the idea of distributed apps in mind. So each app's replica is replaceable and can handle requests in parallel with other ones. The 12-factor app idea was considered at the time of the creation of this application.

The project is divided into 4 layers: Entrypoint, Application, Domain and Infrastructure.

Entrypoint: the HTTP receiving layer, with all the code that helps to express certain expectations from an HTTP request (validation, methods, authentication annotations and such)

Application: the orchestration method. Roughly speaking, all the code that is deeper than the entrypoint but not the business rules yet. Basically, application layer is responsible for calling certain domain invariant operations.

Domain: where the business rules live. Minimum items in order? No clients younger than 18 years old? Time reservation conflicts should not be possible? All of it is Domain. Domain depends on nothing.

Infrastructure: a box of wires. The most low-level code that puts together various technologies and program parts. Specific implementations of agnostic repositories also belong to here. Also, Spring configs live here.

[More about my grasp on the software architecture >>](../../../ARCHITECTURE.md)

## Hibernate

Every ORM brings the trade-off of permormance. To a certain point, it's purely possible to have both code that's easy to write and to maintain and not to witness major permormance spikes. But on a level, there's some sense in getting rid of the ORM and switching to some lower level way of making queries and hydrating data. [jOOQ](https://github.com/jOOQ) is a nice way to go then.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class Users {
UUID.fromString("74e2d42e-7e55-4119-8208-947c06cfe987"),
"Chuck",
"Norris",
"chuck.norris@fromjimmy.com",
"chuck.norris@nikitades.com",
List.of(JwtRole.Manager),
"VIOLENCE_IS_MY_LAST_OPTION",
List.of("openid", "profile", "email")
Expand All @@ -36,7 +36,7 @@ public class Users {
UUID.fromString("b020740b-ea79-447e-acb9-28c539cca909"),
"Keanu",
"Reeves",
"keanu.reeves@fromjimmy.com",
"keanu.reeves@nikitades.com",
List.of(),
"I_KNOW_KUNG_FU",
List.of("openid", "profile", "email")
Expand All @@ -49,7 +49,7 @@ public class Users {
UUID.fromString("903df77b-f156-4fcc-bf2e-907b20b05e36"),
"Thomas",
"Felton",
"thomas.felton@fromjimmy.com",
"thomas.felton@nikitades.com",
List.of(),
"ILL_TELL_MY_FATHER",
List.of("openid", "profile", "email")
Expand Down
5 changes: 5 additions & 0 deletions apps/backend/tg-notifier/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Cars Reservation System - Telegram Notifier

It is a small sidekick project with the only goal to send notifications to the Telegram channel of mine. It receives messages over the AMQP bus and performs the notification. API application produces messages on an occasion of event (for instance, when a new car reservation is made).

Spring Boot 3.0.2.
Loading

0 comments on commit 627de57

Please sign in to comment.