2024-09-30 19:19:59 +00:00
|
|
|
|
# howmuch
|
|
|
|
|
|
2024-10-03 21:22:28 +00:00
|
|
|
|
<!--toc:start-->
|
|
|
|
|
- [howmuch](#howmuch)
|
|
|
|
|
- [Project Diary](#project-diary)
|
|
|
|
|
- [2024/09/30](#20240930)
|
|
|
|
|
- [2024/10/01](#20241001)
|
|
|
|
|
- [Config](#config)
|
|
|
|
|
- [Business logic](#business-logic)
|
|
|
|
|
- [Startup framework](#startup-framework)
|
|
|
|
|
- [2024/10/02](#20241002)
|
|
|
|
|
- [Logging](#logging)
|
|
|
|
|
- [Version](#version)
|
|
|
|
|
- [2024/10/03](#20241003)
|
|
|
|
|
- [2024/10/04](#20241004)
|
|
|
|
|
<!--toc:end-->
|
|
|
|
|
|
2024-09-30 19:52:50 +00:00
|
|
|
|
A tricount like expense-sharing system written in Go
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
It is a personal project to learn go and relative technologies.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Project Diary
|
|
|
|
|
|
|
|
|
|
### 2024/09/30
|
|
|
|
|
|
|
|
|
|
The idea comes from a discussion with my mom. I was thinking about doing
|
|
|
|
|
some personal budget management thing but she brought up the expense-sharing
|
|
|
|
|
application that could be a good idea. I explained why it was a terrible idea
|
|
|
|
|
and had no value but in fact it was a really a good idea.
|
|
|
|
|
|
|
|
|
|
First I have to set up a web server. I'm thinking about using `gin`, since I
|
|
|
|
|
have played with `chi` in other projects.
|
|
|
|
|
|
|
|
|
|
Then I have to add some basic support functions like system `logging`,
|
|
|
|
|
versioning, and other stuffs.
|
|
|
|
|
|
|
|
|
|
Next I need to design the API.
|
|
|
|
|
|
|
|
|
|
- User management: signup, login, logout.
|
|
|
|
|
- A logged-in user must be able to:
|
|
|
|
|
- create an event
|
|
|
|
|
- add other users to that event
|
|
|
|
|
- A user can only view their own events, but not the events of other users'
|
|
|
|
|
- A user can add an expense to the event (reason, date, who payed how much,
|
|
|
|
|
who benefited how much)
|
|
|
|
|
- Users in the event can edit or delete one entry
|
|
|
|
|
- changes are sent to friends in the event
|
|
|
|
|
- User can get the money they spent themselves and the money they must pay
|
|
|
|
|
to each other
|
|
|
|
|
- User can also get the total amount or the histories.
|
|
|
|
|
|
|
|
|
|
That is what I thought of for now.
|
|
|
|
|
|
|
|
|
|
Thus, Besides a web server, I must have a database that can store all the
|
|
|
|
|
data. ex. PostgreSQL. I need a message queue system (RabbitMQ?) to handle
|
|
|
|
|
changes for an event. That will results in a messaging service sending emails.
|
|
|
|
|
|
|
|
|
|
I also want to use `Redis` for cache management.
|
|
|
|
|
|
|
|
|
|
What else?
|
|
|
|
|
|
|
|
|
|
`OpenAPI` + `swagger` for API management.
|
|
|
|
|
|
|
|
|
|
And last but not least, `Docker` + `Kubernetes` for the deployment.
|
|
|
|
|
|
|
|
|
|
That is what I am thinking of for now. I will note down other ideas during
|
|
|
|
|
the project.
|
2024-10-01 11:36:57 +00:00
|
|
|
|
|
|
|
|
|
### 2024/10/01
|
|
|
|
|
|
|
|
|
|
A Go application has 3 parts:
|
|
|
|
|
|
|
|
|
|
- Config
|
|
|
|
|
- Business logic
|
|
|
|
|
- Startup framework
|
|
|
|
|
|
|
|
|
|
#### Config
|
|
|
|
|
|
|
|
|
|
The application provides a command-line tool with options to load configs
|
|
|
|
|
directly and it should also be able to read configs from the yaml/json files.
|
|
|
|
|
And we should keep credentials in those files for the security reasons.
|
|
|
|
|
|
|
|
|
|
To do this, we can use `pflag` to read command line parameters, `viper` to
|
|
|
|
|
read from config files in different formats, `os.Getenv` to read from
|
|
|
|
|
environment variables and `cobra` for the command line
|
|
|
|
|
tool.
|
|
|
|
|
|
|
|
|
|
The execution of the program is then just a command like `howmuch run`.
|
|
|
|
|
|
|
|
|
|
Moreover, in a distributed system, configs can be stored on `etcd`.
|
|
|
|
|
|
|
|
|
|
> [Kubernetes stores configuration data into etcd for service discovery and
|
|
|
|
|
cluster management; etcd’s consistency is crucial for correctly scheduling
|
|
|
|
|
and operating services. The Kubernetes API server persists cluster state
|
|
|
|
|
into etcd. It uses etcd’s watch API to monitor the cluster and roll out
|
|
|
|
|
critical configuration changes.](https://etcd.io/docs/v3.5/learning/why/)
|
|
|
|
|
|
|
|
|
|
#### Business logic
|
|
|
|
|
|
|
|
|
|
- init cache
|
|
|
|
|
- init DBs (Redis, SQL, Kafka, etc.)
|
|
|
|
|
- init web service (http, https, gRPC, etc.)
|
|
|
|
|
- start async tasks like `watch kube-apiserver`; pull data from third-party
|
|
|
|
|
services; store, register `/metrics` and listen on some port; start kafka
|
|
|
|
|
consumer queue, etc.
|
|
|
|
|
- Run specific business logic
|
|
|
|
|
- Stop the program
|
|
|
|
|
- others...
|
|
|
|
|
|
|
|
|
|
#### Startup framework
|
|
|
|
|
|
|
|
|
|
When business logic becomes complicated, we cannot spread them into a simple
|
2024-10-03 21:22:28 +00:00
|
|
|
|
`main` function. We need something to handle all those task, sync or async.
|
2024-10-01 11:36:57 +00:00
|
|
|
|
That is why we use `cobra`.
|
|
|
|
|
|
|
|
|
|
So for this project, we will use the combination of `pflag`, `viper` and
|
|
|
|
|
`cobra`.
|
2024-10-01 21:36:22 +00:00
|
|
|
|
|
|
|
|
|
### 2024/10/02
|
|
|
|
|
|
|
|
|
|
#### Logging
|
|
|
|
|
|
|
|
|
|
Use `zap` for logging system. Log will be output to stdout for dev purpose,
|
|
|
|
|
but it is also output to files. The log files can then be fetched to
|
|
|
|
|
`Elasticsearch` for analyzing.
|
2024-10-02 20:21:09 +00:00
|
|
|
|
|
|
|
|
|
#### Version
|
|
|
|
|
|
|
|
|
|
Add versioning into the app.
|
2024-10-03 11:41:32 +00:00
|
|
|
|
|
|
|
|
|
### 2024/10/03
|
|
|
|
|
|
|
|
|
|
Set up the web server with some necessary/nice to have middlewares.
|
|
|
|
|
|
|
|
|
|
- Recovery, Logger (already included in Default mode)
|
|
|
|
|
- CORS
|
|
|
|
|
- RequestId
|
2024-10-03 19:18:07 +00:00
|
|
|
|
|
|
|
|
|
Using channel and signal to gracefully shutdown the server.
|
2024-10-03 19:54:16 +00:00
|
|
|
|
|
|
|
|
|
A more comprehensible error code design :
|
|
|
|
|
|
|
|
|
|
- Classical HTTP code.
|
|
|
|
|
- Service error code composed by "PlatformError.ServiceError", e.g. "ResourceNotFound.PageNotFound"
|
|
|
|
|
- error message.
|
|
|
|
|
|
|
|
|
|
The service error code helps to identify the problem more precisely.
|
2024-10-03 21:22:28 +00:00
|
|
|
|
|
|
|
|
|
### 2024/10/04
|
|
|
|
|
|
|
|
|
|
Application architecture design follows [Clean Architecture](https://manakuro.medium.com/clean-architecture-with-go-bce409427d31)
|
|
|
|
|
that has several layers:
|
|
|
|
|
|
|
|
|
|
- Entities: the models of the product
|
|
|
|
|
- Use cases: the core business rule
|
|
|
|
|
- Interface Adapters: convert data-in to entities and convert data-out to
|
|
|
|
|
output ports.
|
|
|
|
|
- Frameworks and drivers: Web server, DB.
|
|
|
|
|
|
|
|
|
|
Based on this logic, we create the following directories:
|
|
|
|
|
|
|
|
|
|
- `model`: entities
|
|
|
|
|
- `infra`: Provides the necessary functions to setup the infrastructure,
|
|
|
|
|
especially the DB (output-port), but also the router (input-port). Once
|
|
|
|
|
setup, we don't touch them anymore.
|
|
|
|
|
- `registry`: Provides a register function for the main to register a service.
|
|
|
|
|
It takes the pass to the output-port (ex.DBs) and gives back a pass
|
|
|
|
|
(controller) to the input-port
|
|
|
|
|
- `adapter`: Controllers are one of the adapters, when they are called,
|
|
|
|
|
they parse the user input and parse them into models and run the usecase
|
|
|
|
|
rules. Then they send back the response(input-port). For the output-port
|
|
|
|
|
part, the `repo` is the implementation of interfaces defined in `usecase/repo`.
|
|
|
|
|
- `usecase`: with the input of adapter, do what have to be done, and answer
|
|
|
|
|
with the result. In the meantime, we may have to store things into DBs.
|
|
|
|
|
Here we use the Repository model to decouple the implementation of the repo
|
|
|
|
|
with the interface. Thus in `usecase/repo` we only define interfaces.
|
|
|
|
|
|
|
|
|
|
Then it comes the real design for the app.
|
|
|
|
|
|
|
|
|
|
Following the Agile method, I don't try to define the entire project at the
|
|
|
|
|
beginning but step by step, starting at the user part.
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
type User struct {
|
|
|
|
|
CreatedAt time.Time
|
|
|
|
|
UpdatedAt time.Time
|
|
|
|
|
FirstName string
|
|
|
|
|
LastName string
|
|
|
|
|
Email string
|
|
|
|
|
Password string
|
|
|
|
|
ID int
|
|
|
|
|
}
|
|
|
|
|
```
|
2024-10-04 19:19:49 +00:00
|
|
|
|
|
|
|
|
|
Use Buffalo pop `Soda CLI` to create database migrations.
|
2024-10-05 22:17:45 +00:00
|
|
|
|
|
|
|
|
|
### 2024/10/07
|
|
|
|
|
|
|
|
|
|
Implement the architecture design for User entity.
|
|
|
|
|
|
|
|
|
|
Checked out OpenAPI, and found that it was not that simple at all. It needs
|
|
|
|
|
a whole package of knowledge about the web development!
|