{"id":22235312,"url":"https://github.com/williamthome/ebank","last_synced_at":"2025-03-25T09:24:36.875Z","repository":{"id":199932632,"uuid":"703864338","full_name":"williamthome/ebank","owner":"williamthome","description":"A small financial OTP application for personal studies.","archived":false,"fork":false,"pushed_at":"2023-10-17T01:34:04.000Z","size":108,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-30T08:43:21.571Z","etag":null,"topics":["erlang","erlang-learning","erlang-otp"],"latest_commit_sha":null,"homepage":"","language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/williamthome.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["williamthome"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":["https://www.buymeacoffee.com/williamthome"]}},"created_at":"2023-10-12T04:28:32.000Z","updated_at":"2023-10-16T17:45:40.000Z","dependencies_parsed_at":null,"dependency_job_id":"7f9acf40-215b-4cf7-9ddf-6ebe90af56d7","html_url":"https://github.com/williamthome/ebank","commit_stats":null,"previous_names":["williamthome/ebank"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williamthome%2Febank","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williamthome%2Febank/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williamthome%2Febank/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williamthome%2Febank/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/williamthome","download_url":"https://codeload.github.com/williamthome/ebank/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245433175,"owners_count":20614448,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["erlang","erlang-learning","erlang-otp"],"created_at":"2024-12-03T02:12:47.252Z","updated_at":"2025-03-25T09:24:36.849Z","avatar_url":"https://github.com/williamthome.png","language":"Erlang","funding_links":["https://github.com/sponsors/williamthome","https://www.buymeacoffee.com/williamthome"],"categories":[],"sub_categories":[],"readme":"# ebank\n\nA small financial OTP application for personal studies.\n\n## Goals\n\nBe reliable, have no bugs, clean code, be scalable, and use the minor third-party libraries as possible. The idea to use fewer third-party libs is not to `reinvent the wheel`, but to do most of the code by myself. To achieve these goals I do not doubt that Erlang or Elixir are the right choice, so I'm going with Erlang.\n\n## Project structure\n\nThe project is structured to have domain and business logic separated from what it exposes to the world, also has adapters to plug third-party libraries and not have a direct dependency on them.\nI really enjoy how the [Elixir](https://elixir-lang.org/) code is structured, so some concepts and directory structures are based on Elixir libraries or applications.\n\n## Directory structure\n\n```\n.\n|-- src: the source folder, contains the main code.\n    |-- ebank: holds the business and domain code.\n    |   |-- account: the account entity.\n    |   |-- db: database adapters.\n    |   |-- dsl: query adapters.\n    |   |-- json: JSON parser adapters.\n    |   |-- model: behavior that interacts with the database.\n    |   |-- schema: behavior that holds the database schemas.\n    |   |-- server: server adapters.\n    |-- ebank_web: expose the ebank directory to the world.\n        |-- controller: resolves HTTP requests.\n```\n\n## Data validation\n\nThis application uses `changeset`, an Erlang library created and maintained by me. It provides an interface based on the [Elixir/Ecto changeset](https://hexdocs.pm/ecto/Ecto.Changeset.html) library.\nA changeset is required to manipulate any data in the database, in this way, any data is validated before persists.\n\n## Database\n\nThe current database used is [mnesia](https://www.erlang.org/doc/man/mnesia.html). Mnesia it's a built Erlang database that provides:\n\n\u003e - A relational/object hybrid data model that is suitable for telecommunications applications.\n\u003e - A DBMS query language, Query List Comprehension (QLC) as an add-on library.\n\u003e - Persistence. Tables can be coherently kept on disc and in the main memory.\n\u003e - Replication. Tables can be replicated at several nodes.\n\u003e Atomic transactions. A series of table manipulation operations can be grouped into a single atomic transaction.\n\u003e - Location transparency. Programs can be written without knowledge of the actual data location.\n\u003e - Extremely fast real-time data searches.\n\u003e - Schema manipulation routines. The DBMS can be reconfigured at runtime without stopping the system.\n\nMnesia works really well, it's reliable, scalable, and fits great to this simple project.\n\n## Query\n\nThe query system is provided via `DSL` (Domain Specific Language), where Erlang code is compiled into the database syntax. The syntax expects a list of tuples with this format: `{{Table, Field}, Operator, Value}`.\n\n- The `Table` and the `Field` are atoms defined in the schema (see [metaprogramming](#metaprogramming]));\n- The `Operator` is any [Erlang operators](https://www.erlang.org/doc/reference_manual/expressions.html#term-comparisons), like `=:=`, `=/=`, `\u003e`, `\u003c`, etc;\n- The `Value` is any Erlang term or, if it is an atom starting with `@`, it will be considered as a variable that can be defined at the runtime, e.g. `@foo`.\n\nThey can be linked using `orelse` and `andalso`, and to resolve the variables a map with the index of each field of each table evolved in the query is required, e.g. `#{table_a =\u003e #{foo =\u003e 1}, table_b =\u003e #{bar =\u003e 1, baz =\u003e 2}}`, e.g.:\n\n\n```erlang\nTable = mytable,\nIndexes = #{mytable =\u003e #{foo =\u003e 1, bar =\u003e 2, baz =\u003e 3}},\nClauses=[{'andalso', [\n    {{Table, foo}, '=:=', \u003c\u003c\"bar\"\u003e\u003e}, % \u003c- static\n    {'orelse', [\n        {{Table, bar}, '=/=', '@baz'}, % \u003c- variable 'baz'\n        {{Table, baz}, '\u003e=', 0} % \u003c- static\n    ]}\n]}],\nQuery = ebank_dsl:query(Clauses, Indexes),\nebank_db:read(Query, #{baz =\u003e the_baz_value}).\n```\n\nThis project uses [qlc](https://www.erlang.org/doc/man/qlc) to resolve `mnesia` queries, so the output is a [list comprehension](https://www.erlang.org/docs/23/programming_examples/list_comprehensions.html):\n\n```erlang\n[ Mytable || Mytable \u003c- mnesia:table(mytable)\n, ( element(2, Mytable) =:= \u003c\u003c\"bar\"\u003e\u003e\n    andalso ( element(3, Mytable) =/= maps:get(baz, Bindings)\n              orelse element(4, Mytable) \u003e= 0 )\n  )\n]\n```\n\nTo illustrate, the result of the query above in `PostgreSQL` should be:\n\n```sql\nWHERE mytable.foo = 'bar'\n  AND ( mytable.bar != $1 OR mytable.baz \u003e= 0 )\n```\n\n## Metaprogramming\n\nThis project use `parse_transform` to do [metaprogramming](https://www.erlang-factory.com/static/upload/media/1434462166791692seancribbseuc2015.pdf).\nThe reason behind that is to simplify the code and make it more scalable, readable, and easy to maintain.\n\nCurrently, the project contains two modules that provide metaprogramming:\n\n- `ebank_model_transform`: used by the database models. The module that uses it should expose a `-model` attribute. Currently, it compiles the queries to improve performance. Example:\n    ```erlang\n    -model(#{\n        schema =\u003e ebank_account_schema,\n        queries =\u003e [ q_fetch_by_id/0, q_exists/0 ]\n    }).\n    ```\n- `ebank_schema_transform`: used by database schemas. The module that uses it should expose a `-schema` attribute. Currently, it compiles the schema and exposes it on a `schema/0` function in the module. Out of the box, it provides type definitions and validations via changesets for the fields. Example:\n    ```erlang\n    -schema(#{\n        table =\u003e account,\n        fields =\u003e [\n            {id, {integer, [readonly]}},\n            {social_id, {binary, [required, indexed]}},\n            {name, {binary, [required]}},\n            {password, {binary, [required, redacted]}},\n            {created_at, {datetime, [readonly]}}\n        ]\n    }).\n    ```\n\n_NOTE: An Erlang helper library called [parserl](https://github.com/williamthome/parserl) is used to simplify the metaprogramming. I'm the author and it is maintained by me._\n\n## Third-party Deps\n\n- [Cowboy](https://github.com/ninenines/cowboy): HTTP server;\n- [Thoas](https://github.com/lpil/thoas): JSON parser.\n\n## Commands\n\nThe `make prod` command in the root folder starts the application in production mode, `make dev` to start in developer mode, or `make daemon` to start in developer mode and start a hot code reloading.\nThe command starts a server at the `8080` port and these routes are exposed:\n\n- `[POST] /accounts`: create an account if all parameters are valid and only if no one with the desired `social_id` exists;\n- `[GET] /accounts/:id`: return an account if some exists with the informed `id`;\n- `[PATCH] /accounts/:id`: update an account if some exists with the informed `id` (TODO: allow updates only for authorized/logged user(s));\n\n## Disclaimer\n\nThis is an under-development application. Any change can occur without any notice. There are a lot of things to do and improve.\n\n## TODO:\n\n- [ ] Create a table called `transactions` where accounts can share money;\n- [ ] Create a table called `tokens` or `sessions` where the user can log in and perform updates and transactions;\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwilliamthome%2Febank","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwilliamthome%2Febank","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwilliamthome%2Febank/lists"}