{"id":16017356,"url":"https://github.com/robertohuertasm/payments-engine","last_synced_at":"2026-04-18T19:31:15.821Z","repository":{"id":77795575,"uuid":"454872870","full_name":"robertohuertasm/payments-engine","owner":"robertohuertasm","description":"💱 🦀 Simple payment engine to process csv transactions","archived":false,"fork":false,"pushed_at":"2022-02-10T16:02:05.000Z","size":117,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-30T21:38:44.526Z","etag":null,"topics":["cli","csv","payments","transactions"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/robertohuertasm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2022-02-02T17:32:57.000Z","updated_at":"2023-02-17T00:04:39.000Z","dependencies_parsed_at":"2023-04-25T00:03:36.791Z","dependency_job_id":null,"html_url":"https://github.com/robertohuertasm/payments-engine","commit_stats":{"total_commits":8,"total_committers":1,"mean_commits":8.0,"dds":0.0,"last_synced_commit":"88efb66a935b94f0979cd12191352f0c98c78135"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/robertohuertasm/payments-engine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertohuertasm%2Fpayments-engine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertohuertasm%2Fpayments-engine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertohuertasm%2Fpayments-engine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertohuertasm%2Fpayments-engine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robertohuertasm","download_url":"https://codeload.github.com/robertohuertasm/payments-engine/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertohuertasm%2Fpayments-engine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31982442,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T17:30:12.329Z","status":"ssl_error","status_checked_at":"2026-04-18T17:29:59.069Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["cli","csv","payments","transactions"],"created_at":"2024-10-08T16:05:10.591Z","updated_at":"2026-04-18T19:31:15.803Z","avatar_url":"https://github.com/robertohuertasm.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Payments Engine\n\nThis project showcases a simple payments engine made with Rust that processes transactions and offers information about the current balance of the accounts.\n\n## How to run the CLI\n\nThe `payments-engine-cli` binary reads a CSV file with transactions and outputs the current balance of each account to the std out.\n\nIf you want to run it, you can use the following command:\n\n```sh\ncargo run -- transactions.csv\n# or this, if you want to get that info piped into a file\ncargo run -- transactions.csv \u003e accounts.csv\n```\n\nNote that there's already a `transactons.csv` file in the repository if you're curious about the kind of input you should be using.\n\nYou should get something similar to this as a response:\n\n```csv\nclient,available,held,total,locked\n2,0,0,0,true\n1,250.0001,0,250.0001,false\n```\n\n## Architecture\n\nThe project is separated in several `crates`.\n\nThere are several reasons for that:\n\n1. Making the compilation faster while developing.\n1. Making the project more modular.\n1. Helping the project to be more maintainable and extensible.\n1. Making it easy to split responsibilities and avoid unwanted coupling.\n\nThe idea was to provide a **clean architecture**.\n\nIn order to do that, we defined a `Domain` layer exposing domain entities and some `core traits` to be implemented in the other layers.\n\nBasically, we have a `Store` trait which is responsible for persistence and a `Engine` trait which is responsible for the business logic.\n\nThe current implementation just exposes an `In-Memory` store and a simple `Engine` according to some specific business rules but, as we have everything decoupled, we could easily extend this project to provide different kinds of stores (`PosgreSQL`, `Redis`, even a REST API, etc.) and different kinds of engines with some particular logic.\n\nAt the same time, we're using an `async CSV reader/writer` to input and output the information but this could be easily changed to any other kind of data source for the same reasons stated above.\n\nFind below a `Component Diagram` illustrating the architecture.\n\n![Architecture](docs/components.png)\n\n## Async\n\nThe project makes extense uses of `futures` to avoid blocking scenarios as much as possible and although it uses [Tokio](https://docs.rs/tokio/latest/tokio/) for testing purposes, it's not required in order to implement the `Store` or the `Engine` traits, so you could potentially use [async-std](https://docs.rs/async-std/latest/async-std/) instead.\n\nIndeed, [Tokio](https://docs.rs/tokio/latest/tokio/) it's only a hard dependency in the `cli` crate and the `csv reader/writer` crate.\n\nOne of the ideas was to provide a feature for at least the `csv reader/writer` crate to be able to use some other async runtime but I kept it out of the scope for the time being.\n\nOn the other hand, we're also using [async-trait](https://docs.rs/async-trait/latest/async_trait/) to simplify dealing with `traits` and `futures`.\n\n## Testing\n\nAll the different components have their own `tests` to make sure they work as expected.\n\nI put special emphasis on the `payments-engine` crate, as this is the crate holding all the business logic, but all of them are pretty well covered.\n\nPrecisely, given that `testing` was an important part of this project, leveraging `traits` in order to avoid implementation coupling helped a lot.\n\n## Observability\n\nAll the libraries used in this project are using [tracing](https://docs.rs/tracing/latest/tracing/) to provide observability.\n\nThe `cli` has a subscriber that will output all the traces to the std out if the `RUST_LOG` env var is set.\n\nYou can either set it yourself or add an `.env` file to your project with the following content, as we're leveraging the [dotenv](https://docs.rs/dotenv/latest/dotenv/) crate to read the `.env` file and set the `RUST_LOG` env var.\n\n```sh\nRUST_LOG=debug\n```\n\n## Error handling\n\nThe project uses the usual suspects when dealing with `errors`:\n\n- [thiserror](https://docs.rs/thiserror/latest/thiserror/): to easily create custom `Error` types.\n- [anyhow](https://docs.rs/anyhow/latest/anyhow/): for easily handling errors in the CLI.\n\nGenerally speaking, both in the libraries and in the cli binary, all the errors are being traced.\n\nThat being said, according to the specs, the CLI ignores the serde errors and processing errors from the Engine. It only panics in case there's an error while writing the output.\n\n## Other considerations\n\n- In order to keep the decimal precision up to four places past the decimal and avoid possible rounding issues I decided to use the [`rust_decimal`](https://docs.rs/rust_decimal/latest/rust_decimal/) crate.\n- I tried to favour static vs dynamic dispatch as much as possible where it made sense.\n- The different crates are comprehensively documented in order to help both end-users and developers alike. This improves maintainability and helps people to reason about different functions and design decisions.\n- I used several tools while developing like [clippy](https://github.com/rust-lang/rust-clippy), [cargo-make](https://github.com/sagiegurari/cargo-make) and [cargo-watch](https://github.com/watchexec/cargo-watch).\n- The order of the rows in the output CSV file is not guaranteed.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertohuertasm%2Fpayments-engine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobertohuertasm%2Fpayments-engine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertohuertasm%2Fpayments-engine/lists"}