https://github.com/zerc/dotos
POC of distributed todo application
https://github.com/zerc/dotos
Last synced: about 1 year ago
JSON representation
POC of distributed todo application
- Host: GitHub
- URL: https://github.com/zerc/dotos
- Owner: zerc
- Created: 2023-06-25T20:18:22.000Z (almost 3 years ago)
- Default Branch: master
- Last Pushed: 2023-06-25T22:41:03.000Z (almost 3 years ago)
- Last Synced: 2025-01-27T10:08:33.091Z (about 1 year ago)
- Language: TypeScript
- Size: 91.8 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Distributed Todo list
> **Disclaimer** this is not a product but rather proof-of-concept a (somewhat) distributed
> application. It's unlikely it will be of any use to anyone and is not going to be maintained
> going forward.
## Requirements
The application provides two entities - `Todo` and `Category`. A user can create both of them using GraphQL API.
### Functional requirements
* CRUD for `Category`
* CRUD for `Todo`, in particular:
* * query a list (paginated) of todos with filtration by completion
* * query a todo by ID
* * update the todo / mark it as completed / assign to a group
* Integration with another service provider in a way that:
* * todos created in the third-party service are created in the service
* * status of todos is synced between the service and the integration
* The code should be structured
### Non-functional requirements
The tech stack is the following:
* Typescript
* [Apollo](https://www.apollographql.com/docs/react/get-started) as GraphQL server
* [Prisma](https://www.prisma.io/typescript) ORM
* PostgreSQL database
The code should be structured to allow a small team (~10ppl) to be effective in contributing to it.
### Assumptions
**There can be more than one integration**
This would mean the following:
* Content and the status of the todos should be cross-synced between all the integrations and the local service.
* There will be no "linking" of the todos from different integrations if they were created *before* the integration was enabled i.e. it could create duplicated entries after a new integration is added. The approach was chosen for the sake of simplicity.
> In the real product, however, there could be a setting exposed where a user could specify how to handle this case (it could be part of the process of enabling the integration by the user)
**Other**
* No public API for enabling / disabling / configuring the integrations.
* No authorisation && any user-management functionality
* No deployment setup for production. The service is expected to run locally in the development mode.
## Outcome
### Design
A high-level overview of the design:

A few things to note:
* Database is shared between services for simplicity. However, each component uses a separate set of tables without foreign keys between them. Also, the code is structured in a way that it is easy to configure different databases per component if needed.
* There isn't much difference between **our** service and **3rd-party** integraiton. Each component encapsulates its logic but uses a predefined set of events to communicate with others.
* Every component
* * should store any data required to ensure todos are linked together but do not leak anything outside
* * can have its way to fetch data e.g. webhooks, polling or API
Data flow between components can be seen like this:

### Specifications
The service has two integrations:
* `dummy` - generates a random todo and submits it to the bus.
* `todoist` - a simple integration with [Todoist](https://todoist.com/). It only syncs `content` and completion of the items from the default project via [Sync API](https://developer.todoist.com/sync/v9/#items)
A little demo:

### Shortcuts
Here are the things I'd like to do but decided to de-prioritise due to limited time:
* **There are no tests**. There probably a few bugs because of that. I planned to use [jest](https://jestjs.io/docs/getting-started) for that.
* Data consistency. The current implementation does not tolerate well when third-party service is not available and it may lead to data inconsistency. One way this can be addressed is by replacing the in-memory channel implementation for the [message bus](https://github.com/Dashlane/ts-event-bus) to ensure it can retry `onError` and persist events. However, the application is designed in a way that can be improved without rewriting everything.
* `uuid4` is used to generate a distributed ID for the todos stored locally (in the API layer). In the production system, however, there might be a need for a more sophisticated solution to ensure data integrity.
* Data design could be improved in many ways e.g. index for search optimisation (FTS for example), more complex query builder to avoid extra queries etc.
## Local setup
### Integrations
#### Todoist
Create a [new app](https://developer.todoist.com/appconsole.html) and get **Test token** to get started quickly:
```shell
export TODOIST_TOKEN=
```
### Database
To start the database run.
```shell
docker compose up -d
```
It may take a few seconds for the database to start, check logs - `docker compose logs -f db`.
The admin interface will be available on http://localhost:8080/ and credentials can be found [here](./.env).
### Install and run
```shell
npm install
npx prisma db push
npm run start:app:dev
```
By default `dummy` integration is enabled and it will push one todo item which then will be synced between the API layer and Todoist (if enabled).