<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Willem Pino</title>
    <description>The latest articles on Forem by Willem Pino (@wpino).</description>
    <link>https://forem.com/wpino</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F675067%2Fc7f335ef-baa3-4986-9e2b-668224edf4e6.png</url>
      <title>Forem: Willem Pino</title>
      <link>https://forem.com/wpino</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/wpino"/>
    <language>en</language>
    <item>
      <title>Design to Duty: The Accounting and Reporting Systems at Adyen</title>
      <dc:creator>Willem Pino</dc:creator>
      <pubDate>Mon, 23 Aug 2021 13:51:43 +0000</pubDate>
      <link>https://forem.com/adyen/design-to-duty-the-accounting-and-reporting-systems-at-adyen-3fae</link>
      <guid>https://forem.com/adyen/design-to-duty-the-accounting-and-reporting-systems-at-adyen-3fae</guid>
      <description>&lt;p&gt;In this blog, we will have a closer look at how we make decisions around our accounting system and how it evolved as a consequence. We will do the same for our reporting and analysis frameworks.&lt;/p&gt;

&lt;p&gt;This is part two of a series. So, if you have not done so already, it might be nice to start with the &lt;a href="https://www.adyen.com/blog/design-to-duty-adyen-architecture" rel="noopener noreferrer"&gt;first blog&lt;/a&gt;. In that one, we talked about what Adyen does at a high level, how we think about choosing between home-grown and open-source software, and how this shaped our edge services.&lt;/p&gt;

&lt;p&gt;The themes that were laid out in &lt;a href="https://www.adyen.com/blog/design-to-duty-adyen-architecture" rel="noopener noreferrer"&gt;part one&lt;/a&gt; will return and be referenced in this blog. If you didn’t read the first one, it might be that some context is missing.&lt;/p&gt;

&lt;h1&gt;
  
  
  Accounting system
&lt;/h1&gt;

&lt;p&gt;Once we have processed a payment, the next step is accounting for it. This is needed because after processing transactions, we receive the money from our partners and we need to determine how much to settle to each merchant. Of course, we also need it for reporting.&lt;/p&gt;

&lt;p&gt;For every payment that enters the system, we do double-entry bookkeeping. The way we ensure that we do so correctly is quite unique to Adyen. The only way to add new records to the accounting system is by means of templates. A template in this context is a recipe that takes certain amounts and accounts as input and converts them into specific journals that can be inserted into the ledger.&lt;/p&gt;

&lt;p&gt;These templates are mathematically verified. To achieve this, we represent the amounts that serve as inputs by logical entities and prove that every combination of amounts will result in a net sum of 0. This verification is fully automated and runs on every change to the templates.&lt;/p&gt;

&lt;p&gt;All of this means we can guarantee that if at any time, we sum up all the records in the accounting system, the result will always be 0. Combine this with the aforementioned double-entry bookkeeping, and it means for every euro that ever went through Adyen, we know exactly where it came from and where it went.&lt;/p&gt;

&lt;p&gt;We leverage this same system of accounting for our banking platform.&lt;/p&gt;

&lt;p&gt;It might be superfluous to mention it, but our accounting framework is also written in-house. Here the choice was evident, it is as core as can be to our business, and nothing in the open-source landscape came close to what we wanted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Underlying Database
&lt;/h2&gt;

&lt;p&gt;Having all these nice things does come with some cost. Over the lifetime of a payment transaction, about 50 rows have to be inserted into the accounting database. This means that per second, just for the accounting system, the amount of inserts is an order of magnitude higher than the hundreds of transactions we process every second. Some time ago these thousands of inserts a second started to cause issues for our PostgreSQL database. &lt;a href="https://www.adyen.com/blog/updating-a-50-terabyte-postgresql-database" rel="noopener noreferrer"&gt;This blog&lt;/a&gt; has more information on maintaining such large PostgreSQL databases.&lt;/p&gt;

&lt;p&gt;We had already split a reporting database from our accounting database to minimize reads (more on this below), but at some point, even with mainly inserts and updates, the database didn’t scale anymore. This is when we decided to shard our accounting database into several accounting clusters.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2000%2F0%2ArTo_S_8bcqd66hRP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2000%2F0%2ArTo_S_8bcqd66hRP.png" alt="accounting DB"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sharding strategy
&lt;/h2&gt;

&lt;p&gt;Now we have several accounting databases, or clusters, processing side-by-side. We considered incorporating domain knowledge into the sharding strategy, but, for several reasons, eventually decided to go for a simple round-robin strategy with some parameters we can adjust.&lt;/p&gt;

&lt;p&gt;First of all, every rule we considered would bring its own problems. For example, if we would put each processing merchant in one database, you still need to go to every shard when you need aggregate data to send to our payment method partners. The same holds the other way around. If you split by payment method you still need to go to every shard when you need aggregate data on the merchant level.&lt;/p&gt;

&lt;p&gt;Incorporating business logic would also complicate the routing, a round-robin strategy is very simple and robust and you do not have to think about balancing your shards. Finally, you lose a lot of flexibility. At the moment we can just add a new cluster whenever we need more capacity or remove one from the routing when we see strange behavior we want to investigate.&lt;/p&gt;

&lt;p&gt;In the end, we decided the benefits domain knowledge routing would offer were not worth the loss of flexibility and increased complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migration
&lt;/h2&gt;

&lt;p&gt;The migration to a sharded accounting database was quite painful. This was due to two things. First of all, the accounting logic in the code, pretty heavily embedded in any payments processor, working on the assumption there was just one accounting database. This had to be rewritten.&lt;/p&gt;

&lt;p&gt;As an example, consider a payout to a merchant. This needs to come from one account. However, because you processed these transactions on different clusters, the money needs to be transferred from one cluster to the other in order to end up in the same account. To do this without compromising on the strict correctness requirement was quite difficult. In the end, we created several jobs and processes that use back and forth messages between the clusters to keep everything aligned.&lt;/p&gt;

&lt;p&gt;The second complicating factor was that, if we received reports on processed transactions from our partners, we needed to match them to transactions in different clusters. Instead of parsing a file and matching it directly to the transactions, we introduced a two-step framework that would first parse a file and then split it into the relevant parts for each cluster. The second step was to match the relevant transactions within the clusters.&lt;/p&gt;

&lt;p&gt;Whereas the first problem was solvable in a generic way, the reconciliation needs to happen for a lot of different very custom integrations so this was a real team effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future challenges
&lt;/h2&gt;

&lt;p&gt;From a scaling perspective, the risk that this approach introduces is that any process that depends on all accounting databases being up to date or available becomes a potential liability.&lt;/p&gt;

&lt;p&gt;Historically, processes such as our back office (admin area) would interface directly with the accounting databases to display data or to make corrections. If one of the accounting clusters is not reachable this cannot be done anymore.&lt;/p&gt;

&lt;p&gt;This is not a problem when there are only a few clusters, but as the number of accounting clusters grows, the chance of any of them being unavailable, planned or unplanned, grows with it. This means that instead of interacting with it directly, we need to do so with an intermediate that will mitigate this risk.&lt;/p&gt;

&lt;p&gt;Also, rebalancing the clusters is something that is essential for traditional sharding (inside one database) but has not been implemented yet for this setup. If we add a new cluster, it will be empty while the old clusters keep growing. How do we avoid the original clusters becoming too big?&lt;/p&gt;

&lt;h1&gt;
  
  
  Connecting the edge services with the accounting system
&lt;/h1&gt;

&lt;p&gt;Talking so casually about the downtime of our accounting clusters hints at different priorities in this part of the system compared to our edge systems discussed earlier. Whereas for the latter the priority was uptime, for the accounting systems we don’t mind if they are down for a little bit as long as we can guarantee their reliability and consistency. For me, it was interesting to see how these systems are tied together.&lt;/p&gt;

&lt;p&gt;In a very naive solution, these conflicting principles cause a conflict. The PAL would forward the payment to the accounting system to save it. If something goes wrong, do you still save the payments with a risk of inconsistent data, compromising on the consistency priority of the accounting system? Or do you stop processing, compromising on the uptime priority of the frontend? We went for a failsafe in between.&lt;/p&gt;

&lt;p&gt;The Payment Processing Layer (or PAL, see part one) saves the payment in a local database and a separate process picks it up when possible. This process will run behind if we have doubt about whether we can guarantee consistency in the accounting or if an accounting database is not available but that will not impact the availability of the PAL itself. An added benefit of this setup is that if the PAL crashes, no payments are lost because the queue is stored in a database and not in memory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2000%2F0%2A9Y9apDUCsAkJiMMt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2000%2F0%2A9Y9apDUCsAkJiMMt.png" alt="Queue processor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.adyen.com/blog/The-Adyen-way-of-engineering--we-design-for-20x" rel="noopener noreferrer"&gt;In another blog of ours&lt;/a&gt;, you can find more info on how this asynchronous queue between the different layers works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technology choices
&lt;/h2&gt;

&lt;p&gt;The large accounting database and the short-lived queue both use a PostgreSQL database. This was again a pragmatic choice. In the beginning, the requirements on the system were not that high, so we went for a one-size-fits-all solution for databases. It might be that better solutions for queueing were available at that time or since, but we felt that it was not worth the additional complexity they would add to our system.&lt;/p&gt;

&lt;p&gt;We have since been pleasantly surprised by how well PostgreSQL has performed for both use cases. At this point, we have dozens of local databases that can be instantiated dynamically at application startup and transactional databases of hundreds of terabytes running on the same technology.&lt;/p&gt;

&lt;p&gt;This shows that specialized solutions that were designed to tackle specific problems that occur at very large scales might not be needed for smaller applications (or even quite large ones) while they often do add a lot of complexity. Of course, this is always a balancing act, because the solution might have nice specialized features and it is comforting to know that it will definitely scale.&lt;/p&gt;

&lt;h1&gt;
  
  
  Reporting
&lt;/h1&gt;

&lt;p&gt;Creating separate accounting clusters creates a new challenge. Namely, how do you generate reports for every merchant and payment method every day from all these data sources?&lt;/p&gt;

&lt;p&gt;We did have a reporting database where we saved the relevant data in a denormalized form. Initially created to minimize reads on the accounting database. However, relying on this might work for a while, but eventually, it would be just another bottleneck.&lt;/p&gt;

&lt;p&gt;For this part of the system, the priority was throughput. We decided to stream the data from the clusters and to have specialized processes consume and preprocess the data for specific use cases. This way there are only a limited number of processes that have to read the stream and when the report is generated a lot of the work is already done.&lt;/p&gt;

&lt;p&gt;The reporting tables can be split over as many databases as needed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2000%2F0%2AtjrZULUnc1zNeTdU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2000%2F0%2AtjrZULUnc1zNeTdU.png" alt="Reporting DB"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the time a lot of different technologies were considered for this, especially Kafka. Our current processes were under quite some pressure so time was of the essence. We needed a high throughput, low latency streaming framework that could ensure exactly-once processing, even if processes crashed.&lt;/p&gt;

&lt;p&gt;We scoped a lot of open source technologies but none offered the feature set we were looking for. For example, exactly-once delivery was not yet supported by Kafka.&lt;/p&gt;

&lt;p&gt;On the other hand, we had a lot of familiarity with another technology that was close at hand and had proven very reliable. At this point, you might not be surprised to learn that this was PostgreSQL. For the same reasons, we used Java to write the application code on top of it.&lt;/p&gt;

&lt;p&gt;Even though we did have to do some rounds of optimizations and we are missing some features, such as topics, we are happy with our choice. The setup stood the test of time even though traffic increased by an order of magnitude.&lt;/p&gt;

&lt;p&gt;Even though we initially chose to write something ourselves, this is not a definite choice. In fact, there is an ongoing discussion whether this framework will allow us to scale another 20x or whether we should swap it out for an open-source solution such as Kafka. The 20x scaling factor is a rule of thumb we often use internally when designing a solution or determining whether we are still satisfied with it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Analysis
&lt;/h1&gt;

&lt;p&gt;For our data analysis setup, we did not build much in-house but chose to adopt industry standards. We run a Spark Hadoop cluster combined with Airflow. Deploying it on our own servers was an effort but now it is running smoothly. There is &lt;a href="https://www.adyen.com/blog/building-our-data-science-platform-with-spark-and-jupyter" rel="noopener noreferrer"&gt;a blog about the initial shift&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Remarkable about the setup is that we still use our custom streaming system to verify all the information is actually consumed and correct.&lt;/p&gt;

&lt;p&gt;With the Spark Hadoop cluster in place, the main struggle was to feed the results back into the synchronous systems. &lt;a href="https://www.adyen.com/blog/predicting-and-monitoring-payment-volumes-with-spark-and-elasticsearch" rel="noopener noreferrer"&gt;Here&lt;/a&gt; we describe how we did this for monitoring. After this worked for that use case, we expanded it into a generic framework that can also score the machine learning models in real-time.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;The focus of this blog was on payments processing but, as mentioned before, the concepts translate almost perfectly to the other business contexts such as the bank. This is because all systems have high availability and latency requirements on the edge services and strong consistency and reliability on the accounting layer. In the reporting and analysis frameworks, we tie all of the systems together.&lt;/p&gt;

&lt;p&gt;This similarity in architecture, together with our conservative tech stack, allows developers to easily switch between teams. They already know the general design and the technologies used, even if the business context is completely different.&lt;/p&gt;

&lt;p&gt;We hope that this blog made both our architecture and the way we arrived at it clearer. Perhaps it will also influence how you evaluate design choices for your own company in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical careers at Adyen
&lt;/h2&gt;

&lt;p&gt;We are on the lookout for talented engineers and technical people to help us build the infrastructure of global commerce!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.adyen.com/careers/vacancies/development" rel="noopener noreferrer"&gt;Check out developer vacancies&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Developer newsletter
&lt;/h2&gt;

&lt;p&gt;Get updated on new blog posts and other developer news.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.adyen.com/newsletter/developers" rel="noopener noreferrer"&gt;Subscribe now&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>fintech</category>
      <category>adyen</category>
    </item>
    <item>
      <title>Design to Duty: How we make architecture decisions at Adyen</title>
      <dc:creator>Willem Pino</dc:creator>
      <pubDate>Wed, 28 Jul 2021 13:46:55 +0000</pubDate>
      <link>https://forem.com/adyen/design-to-duty-how-we-make-architecture-decisions-at-adyen-4alo</link>
      <guid>https://forem.com/adyen/design-to-duty-how-we-make-architecture-decisions-at-adyen-4alo</guid>
      <description>&lt;p&gt;At Adyen, we have a very pragmatic way of approaching problems. As a result, we use simple tools to achieve great results.&lt;/p&gt;

&lt;p&gt;The goal of this blog is to walk you through the challenges we faced in scaling our system, how we tackled those challenges, and how our system looks because of those decisions. In particular, we will pay attention to the choice between home-grown solutions versus open-source software.&lt;/p&gt;

&lt;p&gt;In the first installment of the blog, we will discuss these topics as they relate to our edge services, and in the second part, we will do the same for our accounting and reporting layers.&lt;/p&gt;

&lt;p&gt;Instead of just explaining how our architecture looks, we thought it would be interesting to explain why our architecture looks that way. At Adyen, we are firm believers in giving developers responsibility. This means responsibility for the design and implementation of a system and also for the security and maintenance of that system. Because the design of our system is done by engineers who are also on duty. These engineers are strong contributors in deciding how to build something, which sometimes leads to counterintuitive results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building home-grown solutions or choosing open-source software
&lt;/h2&gt;

&lt;p&gt;New joiners to Adyen are often surprised about some instances in which we built stuff ourselves. Because of this, we thought it would be interesting to discuss some of these choices while going through our architecture. For an extreme example of building it ourselves, &lt;a href="https://www.youtube.com/watch?v=M8dnvIFD8Cs"&gt;see this short video about why we built our own bank&lt;/a&gt;. Point 3 in &lt;a href="https://www.adyen.com/blog/from-0-100-billion-scaling-infrastructure-and-workflow-at-adyen"&gt;this blog, about the principles we used to scale our platform&lt;/a&gt;, talks more about which technologies we consider for adoption.&lt;/p&gt;

&lt;p&gt;When we are confronted with challenges, the proposed solution is often either to introduce a new tool or framework or to write something in-house. From Adyen’s perspective, writing something yourself will give you more flexibility but it will cost more time and probably have fewer features than an open-source alternative.&lt;/p&gt;

&lt;p&gt;The usability of an open-source option is likely higher due to better documentation and a larger community, but it might be more complicated, because of additional features that we don’t need and investments in training the people that need to work with it. The security of an open-source option will probably be better because many people vetted it, but the attack surface is also almost always larger.&lt;/p&gt;

&lt;h3&gt;
  
  
  Our view on vendor solutions
&lt;/h3&gt;

&lt;p&gt;A lot of businesses will also consider vendor solutions, so do we. However, we want to focus on the core flows in our system, and for those, we never choose a proprietary solution because we want to have full control.&lt;/p&gt;

&lt;p&gt;We buy instead of build, if the use case is peripheral, it does not have to be real-time, we do not have to embed it, or if it is a good value proposition. An example of this would be some of our KYC background checkers. Of course, avoiding lock-in is an important consideration here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adyen at a glance
&lt;/h2&gt;

&lt;p&gt;Adyen is in the business of processing payments. We receive a payment from a merchant; we send that payment to a third party (such as a credit card company) and we return the result. This happens hundreds of times a second, 24/7. We also keep track of all the transactions so we can pass along the money to the merchant once we receive it from the bank. Of course, we also provide reporting to our merchants.&lt;/p&gt;

&lt;p&gt;We do this for hundreds of billions of euros every year. In the last couple of years, we have introduced additional products such as &lt;a href="https://www.adyen.com/issuing"&gt;card issuing&lt;/a&gt;, &lt;a href="https://www.adyen.com/blog/adyens-banking-license"&gt;a bank&lt;/a&gt;, and &lt;a href="https://www.adyen.com/our-solution/marketplaces-and-platforms"&gt;Adyen for Platforms&lt;/a&gt;, which helps platform businesses like ride-sharing or marketplaces. We do all of this on a single platform, in a single repository (monorepo), almost exclusively written in Java.&lt;/p&gt;

&lt;p&gt;Our system is divided into several parts that function relatively independently from each other. They are split along business domains. For example, we have one part centered on payment processing and another part centered on the bank. In the data layer, they are tied together. The same design principles are applied for each of the subsystems. So while we mainly cover payment processing in this blog, the architecture is similar across the board.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h-rqI1zF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1400/0%2A7aZWamzeFUW4U5k9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h-rqI1zF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1400/0%2A7aZWamzeFUW4U5k9.jpg" alt="architecture flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A payment sent in by a merchant will arrive at our edge layer. Here we will do synchronous processing, contact a payment method if needed, and return the result to the merchant again. Availability and low latency are paramount here. In parallel, we sent this payment to our backend systems where we store it in our accounting system. Accuracy and reliability are the key priorities in this part of the system. Finally, the payment ends up in our data processing layer, where the throughput becomes a major concern. We will go through each of these layers, discussing the choices we made that shaped them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Edge services
&lt;/h2&gt;

&lt;p&gt;Every API call to our systems goes through our edge services first. The payment can come either from a &lt;a href="https://www.adyen.com/pos-payments"&gt;payment terminal&lt;/a&gt;, from a &lt;a href="https://www.adyen.com/our-solution/online-payments"&gt;mobile application&lt;/a&gt;, via a direct API call, or from a &lt;a href="https://www.adyen.com/pay-by-link"&gt;payment page hosted by us&lt;/a&gt;. The Payments Acceptance Layer (PAL) is a crucial service in our edge layer. All payments pass through it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--okI5DKmi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/2000/0%2AwhFzZqns8IhV9H3q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--okI5DKmi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/2000/0%2AwhFzZqns8IhV9H3q.png" alt="architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This application will send the payment to our other internal services. These other services can be a risk engine, a service for saving or generating recurring payment tokens, or a service for computing which connection will lead to the highest authorization rate. It will also contact (through an intermediary service) the partner payment method or scheme that processes the payment.&lt;/p&gt;

&lt;p&gt;An important design feature is that all payments are abstracted at the PAL so the system can treat them as equal. There are, of course, differences between them. Some will have additional metadata (for Point of Sale transactions this might be the ID of the terminal that processed them). However, they all go through the same system and are stored in the same databases.&lt;/p&gt;

&lt;p&gt;The engineers who handled the initial design had already gained experience at a previous payments company. In that company, a payment that would come into the system would keep some state in the edge layer. If a new payment would arrive that modified the original payment, for example, a refund, it could immediately be processed as all the required information was already stored in the edge layer.&lt;/p&gt;

&lt;p&gt;The problem with this setup is twofold. An application could not go down for maintenance or can crash without affecting our ability to process transactions. The other problem is that a new machine could not immediately process the same volume as an old application. Some transactions needed to go to a specific instance. The state in the application made each instance unique.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stateless services
&lt;/h3&gt;

&lt;p&gt;Taking a step back, it is possible to see why we did it differently at Adyen. The priority for this part of the system is to be highly available and have low latency. We should always be able to accept payments and process them as fast as possible. Instead of keeping the state in our edge layer, we decided to process modifications asynchronously, which keeps the edge layer stateless.&lt;/p&gt;

&lt;p&gt;As a result, any PAL instance can be shut down without impacting our ability to process payments, and a new PAL can immediately be as effective as the other PALs already running. This makes our horizontal scaling linear. In other words, if one PAL can process X payments per second, two PALs can process 2X payments per second. This mechanism has been basically unchanged since the start of the company, testifying to its power.&lt;/p&gt;

&lt;p&gt;The fact that the edge services are stateless means that they cannot write directly to centralized databases. This is not only beneficial for scaling the system but also a very nice security feature. All our externally exposed systems are prohibited from writing to central databases, reducing the risk of attackers compromising or stealing valuable data. By ingraining a strong sense of security into developers, we can have security by design, instead of having to retroactively patch holes in the system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Distributed databases
&lt;/h3&gt;

&lt;p&gt;More recently we faced the challenge of making our payments API idempotent. This means that if a merchant sends us the same exact payment twice, we should only process it once but return the same response in both cases.&lt;/p&gt;

&lt;p&gt;As you now know, we do not want to achieve this by restricting payments of some merchants to certain machines, as this would mean the machines are no longer linearly scalable. The information needs to be available locally, so we eventually decided on integrating &lt;a href="https://www.cockroachlabs.com/"&gt;Cockroach&lt;/a&gt;, a distributed database, with our PALs.&lt;/p&gt;

&lt;p&gt;We could have built something ourselves here (probably on top of PostgreSQL) but this is really not our core business and there were several open-source options that satisfied our criteria. Nevertheless, getting properly acquainted with the DB and optimizing it to the point where we were satisfied with it required a substantial effort. For another example of a decision between open-source and building ourselves, see &lt;a href="https://www.adyen.com/blog/the-adyen-way-of-engineering-oss-or-built-in-house"&gt;this blog on our graph risk engine&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Future Iterations
&lt;/h3&gt;

&lt;p&gt;The next big step for our edge services would be to scale them dynamically. We manage our own infrastructure in several data centers around the world and have bare metal available, but the hardware and software are still tightly coupled.&lt;/p&gt;

&lt;p&gt;The reasons we are not deploying to the cloud are part historical, part legal, and part technical. In part, because we now need dynamic scaling, we are moving towards running our system on an internal cloud. A blog on the containerization effort is forthcoming.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope this blog shows how we make decisions at Adyen about how to scale and which technologies to use. In all our choices we are highly pragmatic, adhering to the KISS (keep it simple, stupid) principle almost to a fault. This sometimes runs counter to established doctrines in the industry but it shows that you can build a solid company with a small set of trusted technologies.&lt;/p&gt;

&lt;p&gt;In the next blog, we will look at the architecture of our accounting and reporting systems. Unlike our edge layer that has remained relatively static, we actually had to redesign the accounting system several times. In that blog, we will also expand more on building home-grown solutions versus choosing open-source software.&lt;/p&gt;




&lt;h3&gt;
  
  
  Technical careers at Adyen
&lt;/h3&gt;

&lt;p&gt;We are on the lookout for talented engineers and technical people to help us build the infrastructure of global commerce!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.adyen.com/careers/vacancies/development"&gt;Check out developer vacancies&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Developer newsletter
&lt;/h3&gt;

&lt;p&gt;Get updated on new blog posts and other developer news.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.adyen.com/newsletter/developers"&gt;Subscribe now&lt;/a&gt;&lt;/p&gt;

</description>
      <category>fintech</category>
      <category>architecture</category>
      <category>payments</category>
    </item>
  </channel>
</rss>
