How to leverage AWS to create a distributed Foreign Exchange (FX) service that exchanges currencies in real-time.
At Runa’s Treasury team, our mission is to make the flow, accounting, and reporting of financial value seamless and efficient for our customers with 100% confidence at scale. We are excited to announce a new FX service to provide an effortless and instant currency exchange experience on the platform.
Runa FX delivers real-time currency exchange using AWS technologies. The long-term vision of this functionality is to provide customers with the ability to place orders in their base currency to buy assets in different currencies, while conversions are done on the fly behind the scenes. Therefore, our service needs to be scalable and highly capable to handle thousands of transactions per second.
Let’s dive into some of the big-bang decisions we have made to design this service with a low-code approach.
Choosing the correct database was critical in our design to ensure low latency and high throughput. The main database operation was to retrieve quotes by different parameters. We initially considered DynamoDB and AuroraDB because they align with Runa’s serverless architecture vision. However, after weighing the options, we opted for DynamoDB, as it met our requirements for low latency, high availability, and scalability and did not require ACID transactions.
Another consideration was DocumentDB, which offered flexibility with schema and queries. It is a fully managed database with minimal configuration and is highly available. Choosing this approach would encourage us to fail fast as we can always redesign the schemas per new requirements whereas in DynamoDB it is critical to get the design right from the start. However, a deeper analysis revealed that DocumentDB’s scalability was limited compared to DynamoDB for our use case. In DocumentDB, the primary database instance handles both read and write operations, which could become a bottleneck for our write-heavy service.
Ultimately, DynamoDB proved to be the best fit for our FX service, offering low latency and high scalability, while ensuring the data was highly available.
Our team’s goal from the start was to create a new and independent FX service from scratch. We considered using Lambdas in conjunction with Simple Notification Service (SNS), Simple Queue Service (SQS), or AWS Step Functions to manage workflows between components. AWS Step Functions emerged as the clear choice for our solution because our use case did not require a complex event-driven architecture. Instead, our priority was to have a simple and sequential design that was easy to debug. Our previous experience showed us that managing a serverless system with too many Lambdas can be challenging and difficult to scale.
The power of Step Functions lies in its ability to act as a ”function orchestrator” that connects the different steps of our application. The steps can handle tasks such as decision-making, retries, parallel processes, error handling, and timeouts, which results in less code to write and maintain. Step Functions also offer direct integrations with other AWS services such as DynamoDB, S3, EC2, and Batch, allowing us to reduce the number of Lambdas we need and improving the overall latency of the FX service.
In the diagram below, we can see an example of our step function setup for exchanging currencies. It is responsible for getting a quote from a provider, performing calculations, and storing the result in our database. The ability to visualise the steps allows our team to effectively debug, monitor, collaborate, and optimise because each transition can be reviewed in more detail without diving into any code. At each step, we directly integrate with another AWS service, perform data transformations, handle errors, and set up retry logic that can be easily configured.
Step function design to exchange currencies
The primary cloud service provider for Runa is AWS, and different teams and repositories use different Infrastructure as Code (IaC) tools for development and deployment. The four tools in active use are Terraform, SAM, Cloudformation, and the AWS CDK. We used this opportunity to review our existing deployment tools and make a change if needed. After, careful consideration, we went with Terraform for various reasons.
One of the most appealing factors was its declarative language (Hashicorp Configuration Language), which makes development easier, and its ability to use modules, making it easier to reuse code, read, and maintain. Terraform also has a wider range of providers available, which was crucial in choosing it over the other IaC tools.
Additionally, our team considered the rollback mechanism, drift detection, and deployment security when making this decision. While the AWS CDK provides a rollback mechanism, Terraform’s advantages in these other areas made it the preferred choice for this project.
In the end, our team has achieved a low-code solution with only 300 lines of Python code. We utilised the AWS serverless stack to do most of the work for us, eliminating the maintenance and operation hassle that comes with writing code. To tie everything together we used Terraform to create, manage and deploy all of our resources. This low-code solution has several advantages, including reducing the time and resources required for development and maintenance, improving scalability and reliability, and enabling faster iteration and innovation. You can find the final FX architecture below.
High-level FX architecture design