(upbeat music) Today, we're gonna talk about building modern cloud applications. But what we realize is that building modern cloud applications has a lot to do with integration. And I'll explain to you why that is. So here's my sample of modern cloud application. But like, understand, (mumbles), you could almost call this a cloud native. Of course that's not what modern cloud applications look like. modern cloud applications are distributed. They're fine grain. They run in multiple availability zones and across multiple VPCs. So that means they're also connected and integrated. And that's what we wanna talk about today.
My name is Gregor. I'm an Enterprise Strategist at Amazon Web Services, and I do a variety of things. I've worked with our most strategic customers. And I also like to write books. I write books about integration. I write books about architecture. And I write books about cloud strategy. And what we're gonna see today actually pulls from all three of those topics. Now let's start with integration. Integration by no means a new topic. And at the surface, it's a relatively simple topic, right? All you need to do to integrate two systems is you need to connect
System A to System B so the two systems can talk to each other. Now, ironically, this seemingly simple architecture has had decades of evolution, right? Like 40 years almost ago, right? We had ETL, the Extract Transform Load tools. Then came the Enterprise Application Integration, EIA, and EII, that's about when I wrote my patterns book. That gave the base vocabulary to the enterprise service bus, the ESBs. Then we had service oriented architectures, complex event processing, iPaaS in the cloud, service meshes, data meshes, and we'll see what comes next. Well, that's quite a lot of product evolution
for a seemingly simple problem. And of course, to us at AWS, integration is also not new. You might remember that one of the very first services along with S3 and EC2 was SQS, simple queuing service back in 2006. Not so long after, about a couple years later, came the simple notification services. And then we have the workflow API gateway, step functions, MQ, AppSync, EventBridge, AppFlow, and in the end, Amazon AirFlow. So the interesting thing is you see, if anything, the pace is increasing. And there seems to be a lot to be said and done about
integration. Now, one question we have is how do all these different integration approaches relate to each other? Is an ETL is somehow something different than a service path or a service mesh or a data mesh. So how do you structure this space? And a lot of people have had many arguments about this. What I think works best is to consider the level of control that you have over the systems you're integrating and the life cycle. So for example, a migration that might use ETL tools might use Amazon AppFlow for example, is a scenario where you
have very little control over the end points. You might be migrating out of one CRM system into another one, into commercial product, and you do this only one time as a one off thing. So if that's one extreme case of a one-time shot. Now, data synchronization runs more frequently. You're trying to peep to databases, for example, in sync. But you don't need to change that or have a software delivery life cycle. Once you build the system, it's gonna do the synchronization and you're largely fine. Enterprise Service Bus brings the integration closer to other software delivery,
right? It's now starting to really become part of your application because you have more control. You're integrating applications that you're actually building. And then at the very end of the spectrum, the part we're gonna talk about today is how integration really becomes an integral part of your software delivery cycle. So you're deploying integration at the same pace that you're deploying software and you're using the same methods. Perhaps this is one of the reasons we often don't call it integration. We just call it building modern applications. So when you build serverless applications with AWS Lambda, you'll
be naturally using products and services like Amazon EventBridge as an event pass, or you will use AWS Step Functions as an orchestrator. You might not be thinking that you're doing integration, but that's exactly what you are doing just in a very modern fashion. Now, the first message I want to leave you with is that in modern cloud applications, integration is no longer an afterthought. That used to be in the old days with ETL, and we still sometimes do it with cloud migrations, but rather the integration has become an integral part of the way you design
and build and operate applications. Now, when we talk about integration, we saw the picture of two boxes and a line. It tells us that lines play a special role in integration. And to highlight this, let me show you two system architectures. They're very, very simple. And in fact, they consist of the same components, A, B, C, and D. Now the only thing that's different in these two systems is the lines. The one on the left forms what we call a stack, a layered system, the dependencies flow from top to bottom. Has many advantages. It can
replace one system with another one, but it also might incur longer latency and it might have a single point of failure. If C fails, B can no longer talk to D. On the right-hand side, you have the same components, but wired together differently. And you'll find that the system's qualities, the essential defining properties, are exactly the opposite. But now you don't have a single point of failure. A and B can still talk to D whether C is there or not. You have shorter paths, but you have more dependencies. It's harder to replace a component. So
we learned that the lines really define your system's properties. You have the same ingredients, just different lines. So that as a pitch, that the lines are as important as the boxes and perhaps even more so. That's our second lesson, right? How your systems are interconnected defines your system's essential properties, how your system will scale, how resilient it is to failure, how easy it is to change all these things depend on the lines. The lines are integration. So if you wanna look at it from a more definitional point of view, the lines are also an integral
part of what we call software architecture or software systems architecture, right? If you follow one of the most respected definitions about software architecture is all about, it's the elements and the relations among them, the properties of both the elements and the relations. So I have a ground rule that I tend to reject architecture diagrams that have no lines. That might be because I wrote a whole book about integration and about lines, but it's also because, without the lines, you have no idea how your system will actually behave. So we learn that you cannot build modern
systems without lines. And it also means that it's all about how it's put together. So what I say is that great architects, we are like chefs. It's not just about selecting the ingredients. That's nice and that's fun, but in the end, your system's quality is defined by how you put it together. So I invite you to be the chef of your system and think carefully about how you put it together, how you wire it together. Now, as I hinted, there's a lot more between the two boxes and the one line than by that first appear.
So let's look a little bit deeper into what's going on in integration architectures. And as I warned, this has been going for three or four decades. So here are some pictures. And you can probably tell that are about three, well, actually about two decades but we don't wanna make them older than they really are. They're from like 2003 from my book, "Enterprise Integration Patterns". And here we structure integration approaches into four design patterns. We talk about file transfer. Still a lot of financial industry relies on file transfer and protocols like Swift. Then we realize that
file transfer has latency and systems are never quite in sync so we started putting everything into a shared database. That was great because all the synchronization problems went away, but suddenly we found that the system was very brittle because we had introduced too much coupling. So we backed off and we made everything a procedure call. The remote procedure invocation or RPC, we sometimes call it. Now, we no longer have to share database, but then we realized that a synchronous implication isn't very well suited to a distributed system design. Finally, we arrived at asynchronous messaging and
message buses and event buses like we have them with EventBridge. So this could last 20 years or maybe more to figure out, but it tells you how many considerations of coupling, the level of abstraction. Is it synchronous or asynchronous? How timely is the data replication and what complexity do you have in your system? How many of these considerations play into here? Here we are again with our two boxes and one line. And one of the key considerations I just mentioned is coupling. It's hard to talk about integration without mentioning coupling. Now I don't like buzzwords.
Even though I wrote a whole book about this buzzword, I like people to understand what's behind the buzzword. So I define coupling as a measure of the independent variability between systems. If System B can vary, it can change or slow down or move to another location without affecting System A, that means that the systems are loosely coupled. Now that's a nice property because you can change B even to a different component without A ever knowing. Why would we loosely couple everything? The answer is relatively simple. There is also a cost. So design kind of time
cost, you need message channels and common data formats. And there's also a runtime cost. There's marshaling and unmarshaling, data formats, moving data from A to B, there is a runtime cost. So the important part here is coupling isn't binary. Things are not just coupled or decoupled. There's many gradations here, and it's not a single dimension. You can be coupled in many different dimensions. You could be relying on having the same technology in A, B and D. So if you want to change the technology in B to C++, you have a problem because you were too
tightly coupled. You can be coupled to a location, right? You might assume service B lives at a certain IP address. Or if you're a little bit smarter, you have a DNS lookup, but you're still tied to a certain domain name. You might be using common data formats that both sides have to support. Otherwise they can be connected. Then you have to have shared data types. You'll have issues like big NDN and small NDN. Another dimension of coupling. You have semantic coupling. What is called name in one system might not be named in the other system.
Some countries have two last names. Other ones don't have first names. Lot of semantic issues you find when you connect two systems. Temporal coupling is another aspect. If system B is slow, does that mean A has to be slow? Wouldn't be that great? Can't we decouple that? And the list goes on. So we find that this simple buzzword has very important meaning. It has many benefits and it also has a cost. And to decide how loosely or tightly coupled we wanna be, we need to look at all these different dimensions. I actually have another talk
at re:Invent where it's about thinking like an architect. So this is exactly what thinking like an architect is about, you're getting the mini version. You're getting the "Reader's Digest" version here. So what we take away is that there isn't such a thing as the best level of coupling or everything should be decoupled. The lesson is that the appropriate level of coupling depends on the level of control you have over the end points. If you don't have much control, you want to be loosely coupled because things might change without your control, or you might want to
change them and you cannot. But if you have more control, you can actually be more tightly coupled because if B changes you can afford to also change A because you have that level of control. That actually leads us to how we build modern cloud applications. Remember I said modern cloud applications are inherently distributed, but it's our application. We control all the end points. So we can actually afford a higher level of coupling and look at integration from a very different point of view. In fact, the whole point of modern cloud applications of serverless applications is
that they consist of smaller pieces that are loosely joined. Now, how loosely? You decide based on the slides we just saw before, right? You decide how loosely or how tightly you can make it, but either way you will have smaller pieces because smaller pieces have smaller blast radius of failure. They scale more easily and you can more easily deploy updates. So they have many fantastic benefits. And the hint we're getting here is that originally when people talked about serverless, people thought about, oh, this is AWS Lambda, right? This is about a different way I deploy
my application, like a different place to run my code. Now I already hinted that in these kinds of architectures, integration is an integral part, pun intended, of your application delivery. So it shouldn't surprise you that in our service portfolio, our serverless service portfolio, integration is actually a very big part. So serverless isn't just Lambda and Fargate. Serverless is also Amazon EventBridge. It's also the API Gateway. It's SQS, SNS, it's AWS Step Functions, and it's AWS AppSync. So all this is part of making successful, modern serverless cloud applications. And that's our next takeaway, serverless is much
more than just the application runtime. They're inherently integrated. And that's why serverless integration is such an important part of building modern applications. Now we've seen quite a bit of theory here, right? So a lot about integration and software development life cycles and coupling and levels of controls. So it's time for an example. And because I like to have fun, I picked an example from 2003. I mentioned I've been working on this for some time. We'll take an example from the Enterprise Integration Patterns. And it's a fictitious example. It's from a domain that most people easily
understand. It's a home loan application. You would like to buy a home. Since you're unlikely to have that much cash on hand, you need a mortgage. You need somebody to lend you money. You know, the people lending you money want to see your credit worthiness. And there's many banks who might be able to do that. So you usually talk to a loan broker, right? And this broker will check your credit worthiness, pass that information onto the banks. And then you have the banks respond usually with the terms of the loan, by like what's the interest
rate, what's the duration of the mortgage you're gonna get? Well, simple enough, just a handful of components, a few boxes and a few lines. Well, in 2003, I built this with the tools we had available at the time. But we'll see how this makes a great example for serverless implementation as well. To be honest, I wish we have those back in 2003. It is much, much easier, much more productive to build this in a serverless cloud environment. At the heart of this application of this little sample is the interaction between the loan broker and the
banks, because you might not know how many banks there are, and you might not know whether all banks want to actually provide you with a quote. And this is one of the design patterns of the enterprise integration patterns. And all these patterns they have evocative names, so this is called Scatter-Gather. You sent a request to an unknown number of recipients and you aggregate the results back into a single message. It's built out of a broadcast, a Publish/Subscribe messaging, and an aggregator. They broadcast does the scattering, and the aggregator does the gathering. This is what the
integration patterns look like. Now, if we see what design decisions are behind this, again relatively simple diagram, you might now expect that we find more than we initially expect. You see empty space on this slide. There's more to come. So how do we know the recipients? Can any bank just go unsubscribed? Do we have a list of preferred banks? Do we send to some banks first? Do we send it to other banks later? Many, many design decisions. Does the bank have to respond or can the bank say, no, I'm not giving this person a loan
for whatever reason and just not reply? Now, aggregating the banks becomes a little bit more difficult because if we don't know how many banks we have and banks aren't required to respond, we don't know how many responses we should have. So waiting for all might not work, but instead we might time out, but then time out is slow. Not every customer wants to wait, just because we're thinking one more bank might send a code, but actually never does. So we might just wait until we have, let's say two or three responses and call it a
day, right? You might also say if the first quote is a really, really good rate, why wait for the other? It's just, you know, tell the customer, this is the best offer you can get. And the list goes on and on. There are many, many ways that you can aggregate. Again, you might not be surprised anymore, but you find that behind these simple diagrams, these simple boxes and lines are a wealth of design decisions, right? The last step here is, well, once the aggregator has decided that it's done, how does it actually aggregate the responses?
We can just concatenate here's everything I got, that might be wasting space. So it might just pick the best answer for you because you might not want the second best anyway. It might add things up, average, perform some operations. Again, many ways of doing this. And that's what we do with the design patterns. We catalog these kinds of design decisions. So it's easier to build these kind of solutions. Well, and it wouldn't surprise you that a pattern like this maps easily to AWS cloud services. So one way to do this is that with step functions
we have a map state. A map state is essentially a parallel loop. That's why it's called a map. It iterates over a set, and it can iterate over a set of Lambda addresses. So if your banks are lambda functions, you can have a map state where you will fetch the list of lambda functions you like, like the list of banks. And then the map state would iterate over it, actually in a parallel fashion. And it will concatenate the results for you. On this case, you do know how many banks you're talking to and you're likely
requiring the banks to respond because you're doing this in parallel, but synchronously so you know how many responses you have, and you will simply concatenate the responses. But that's not the only way to build this pattern. You can also use EvenBridge. If you don't have a large number of banks, you were limited to five targets per rule for each event bus, well, you can just enter or programmatically choose the banks here and send each bank via, let's say SQS, or with an event. We can send them the request for the log. This case, the aggregation
has to be done separately. The map function does it for us. The EvenBridge is just the outbound part. It's basically the scatter, and we still need to build the gather. Probably the first one you might've thought of when it's about scattering, when it's about sending messages to a large number or a variable number of recipients, of course we have SNS. We have the simple notification systems where basically any bank that has permission can subscribe and this message might go out to hundreds or thousands of banks without any problems. Again, the gathering has to be done
separately. So the design patterns help us express the intent. What are we trying to achieve? Because saying that they're using EventBridge doesn't mean that you're building a scatter-gather pattern. You could be doing a million other things with EventBridge. Certainly with step functions, you could be doing many, many things. So the patterns express the intent, and then we can map the intent to the different implementations. And we can compare the implementations, right? The amount of fan out, right? Like SNS. You can have millions and tens of millions. EventBridge is rather small. And step function is gonna
be somewhere between maybe a few dozen. You can choose whether it's synchronous or asynchronous event-driven. You can see how a bank actually subscribes, right? Whether that's done in the loan broker, where that's done via a subscription. You can catalog these solutions. But you found a much nicer way to saying I am looking to build a similar thing and I'm consciously choosing between these services. But I can express the intent that I have. So the advantage we have with serverless integration is that we can work fine grained. Like if you're honest, making this loop in step
functions isn't probably the best solution. Because if you look from the outside, nobody will know that these step functions sends a message to multiple recipients. Doing this via SNS or doing it via the EventBridge makes it much more explicit. You can see aha, there is three targets. We will be talking to three banks. And because all these services are serverless, it's totally easy to have a new instance. You're not really thinking about, oh, do I need another tool? It's all serverless, it's deployed in seconds. And your intent is expressed much, much better in the final
solution. And of course it also scales better and monitors more easily and is more robust. So there's many reasons to make your implementation choices explicit. So I have mentioned enterprise integration patterns and we looked at scatter-gather pattern, one of the more interesting patterns. Now, just like integration, patterns are nothing new. And the timeline is actually not that far off. Most of us know about design patterns when the so-called "Gang of Four" book came up. That's the design patterns book by four authors, right? And that's a pattern oriented software architecture. Martin Fowler became very famous for
writing "The Patterns of Enterprise Application Architecture" during the heyday of J2EE and Java. Here's my book of about me, and my book that we cross it, "Enterprise Integration Patterns". Now, when people hear patterns, they think of many different things. Is it like a recipe? Is it like a guide book? Is it painting by numbers? It turns out in the end patterns have many different benefits. The official definition is that a known solution to a recurring problem with a given context. And that is true that they are, but they're also bite-sized bits of wisdom. A friend
of mine called them the chicken nuggets or Chicken McNuggets of programming knowledge, the little bite-sized pieces of wisdom about how to implement something and identifying the trade-offs and design decisions as we saw. They're really good at expressing the intent, right? If you have an EventBridge bus, the intent isn't that clear. Okay, you're trying to pass events and decouple, but it could be anything. It could be a scatter-gather, it could be an aggregator, it could be a router. Now you have a vocabulary to express why you're having this EventBridge and what you're achieving with it. And
lastly, you have a vocabulary. When you talk to somebody about, oh, I'm building an aggregator, they will know, aha, what's your complete dysfunction, right? When you finish, how do you actually aggregate the results? They will know what you mean. And instantly you can dive into more interesting design decisions because they know what the patterns and expressions. To me, the most important part of these patterns is that they don't live in isolation. One pattern isn't very interesting. They live in a pattern language and together they help you design systems. Our case that pattern language is organized
chronologically, right? You're sending a message, so you need a message end point, you need to design a message. You put it on the message channel and you route it to the proper destination. You probably need to do some transformation along the end. And you monitor the system along the way. These are the 65 integration patterns and they form a cohesive language to describe integrated systems. We just learned that all modern cloud applications are integrated systems. Now, you find these patterns in the AWS cloud platform in two different places. So the first one is what we
just did, which shows a pattern that we want to implement. And then we make the implementation based on the services. So we use the pattern to express our intent, and then we find the appropriate service. And sometimes we find that the service already implement some of the patterns. And other times we build the pattern on top of the service. So we found that a simple scatter is simple, so a multicast is already built into EventBridge and Publish/Subscribe is built into SNS. So we can choose the pattern and there's a very straightforward mapping to the service.
However, for an aggregator, the gather part of scatter-gather, there isn't a matching service. So I write a Lambda function to implement that. So we use patterns both to describe the capabilities of our services in a technology-neutral way. You can say that a scatter is built into both EventBridge and into SNS. And we also use patterns to describe the solution that we're trying to build, like this loan broker. So here we see the loan broker picture zoomed in one level. It's still the loan broker on the left and there's the banks in the middle. And you
see they have become implemented as Lambda functions. We skip the credit bureau for this picture to make it simpler. But you see how, A, we zoomed in, how we're showing more detail, and you can see how nicely all this maps to AWS cloud services, right? There is SNS, there is Lambda, there's EventBridge, there is SQS and there's more Lambda, and there's even DynamoDB to keep the state of our aggregator. And the orchestration is in step functions. So there's a very nice mapping here, and thanks to the patterns and the icons, we maintain the intent. We
can see what's going on here, but we can also see what services we map this to. So we like these kind of pictures because they show both intent and implementation technology. Now, when we look deeper into how the patterns are implemented in the services, you can actually start to express some of our services with these kind of pattern templates. So Amazon EventBridge is really an event bus. But on the event bus, the first thing you do is you match to an event pattern. Careful here, pattern means in this case, a specific type of message you're
trying to receive. We call this a message filter. You don't respond to every message. You filter the messages you like to respond to. And then you have a recipient list. You have the scatter of the scatter-gather. We call this a recipient list of multiple targets. And then for each target you can pick a transformation. You can have a JSON path expression right in EventBridge that you can configure. So EventBridge is a perfect combination of three patterns and you have a dead letter queue if it turns out that the message can't be delivered. So it's actually
four different patterns. AWS Step Functions does something very similar. For example, when you invoke a Lambda function, you can select the data that is passed to the function. We call that a content filter or a translator, a transformer. And when you get the results back, many, many result events or many result messages have a lot of extra payload, a lot of metadata that you don't want. So Step Functions has a built-in feature to filter the content back down so you just get the elements that you really need. And then you can merge this back with
the current state that you have in the Step Functions. So again, you can express what these services do with these integration patterns. And of course, now it becomes relatively obvious. If I need something that's a recipient list, plus a transformer, well, then EventBridge makes a great way to implement that because the pattern matches very easily to the service. If I need something that does a complex aggregation, then I need to build that myself and I will do that with AWS Lambda in a nice serverless fashion, works very, very nicely. So the patterns allow us to
express more nuances also. So we have a pattern that's called a translator. Well, that just means the message that comes out is different than the message went in. So that's the one I used on the left hand side, because that's the generic case. But you've seen me already use variations of this pattern. For example, the content filter. The message that comes out is a subset of went in. We don't add anything. We just filter out. Or a claim check, which means I store the information somewhere else and just give you a key, just like the
little stuff you get when you check your code or check your luggage. Or I might have a normalized way of translating messages to get them all into a consistent format. So that's the power of the pattern language. I know I have a message translator available to me in EventBridge, but I can use that in different ways. And I have a language that allows me to express what that specific way is. There's 65 of these, right? So there's many nuances that you can express. Now let's take this one level further. We are in the cloud, we're
in serverless land here. So of course, what we do is we automate, right? And one of the popular buzzwords there is IAC, infrastructure as code. I'm actually good friends with Keith Morris who wrote the book and he won't be angry at me when I tell him that the acronym is actually kind of wrong, because in the serverless land, we don't really talk about infrastructure much. It's serverless. And we'll see that a lot of existing automation isn't actually great code either. So let me give you my view on what infrastructure as code or so-called infrastructure as
code looks like in a serverless world. And amazingly, we will find that it has a lot to do with integration. So to me, automation has four different levels. The base level is provisioning, right? You need compute resources or storage resources that you can build on top of, right? That's what classic a IAC does. Now, of course, if you have a compute node, you need to deploy something on it. Otherwise it's not gonna do a whole lot. And I had hinted, automation has a lot to do with integration nearest our lines. This is how things are
wired together. So my trivial example here is I have an API gateway and a load balancer to root request to one of two virtual machine in two instances, right? So I compose this to make sure this actually performs the function that I intended. And more likely than not, the configuration I have on the two nodes are gonna be exactly the same. There's probably a primary and the secondary nodes. Maybe I do some charting, different things. So the last element of my automation stack is configuration. So it's a lot more than infrastructure going on here, right?
The bottom on the provisioning, that's really the one that you would compose, we call infrastructure. So that's why I say IAC is not just infrastructure as perhaps code. So let's look at the code part. A common word in automation these days is declarative provisioning. And what that means is that rather than saying provision me one virtual machine, you declare your desired state. You say, I need a load balancer and I need two virtual machines. And the enormous advantage that has is that your system will know the current state. So if you have one virtual machine,
again, simple example, if you have one small virtual machine, but you need two large ones, the system can figure out on its own that this time it needs to deploy a second instance and upsize an existing instance. Well, the next time you run this grip, the system will know that nothing is to do. And when you say I need three VMs, you will know that two are already there and bring you into define state. This is a fantastic property. And this is probably the biggest advance we made with automation, right? Most JavaScripts are set like
server one, server two. This is not how the cloud works. The cloud defines a desired state. And we call this declarative provisioning because we declare what we want, but we don't prescribe how the system gets there. Now, where this might be confusing is that we often associate this with using declarative programming languages like functional language or documented or dead languages. Many people feel like, well, if I declare my infrastructure, I need to do this in YAML. No, you absolutely don't. You can use any programming language that can define a data structure to do this. My
favorite languages, they're object-oriented. So you can use Python or Java TypeScript or anything you want to actually define this declarative state. So don't believe that declarative provisioning requires you to use a language like by like YAML or JSON. You can use a proper programming language to do that. And that means that if we put serverless and integration and automation together, we can do some super interesting things. So let's see how all these pieces are gonna come together. So here's my simple loan broker. Again, implemented as a serverless, as a serverless system, let's say, Now I
had already said, you don't need to use declarative languages to have declarative provisioning. Now of course, my favorite automation tool is going to be CDK. You know, because with CDK, you have a choice of programming languages. You can use object-oriented languages to do your automation. So I chose a small piece of the automation for this loan broker, and I chose the piece that deploys bank. So we have a method here called createBank, and it takes the parameters that you would expect, right? The bank has a name, it has an ID. It has some business parameters,
again, fictitious domain, right? But we have a sort of rate that it charges, the maximum loan it will service and the minimum credit score that a person needs to have to get a loan for this. And we'll see that it also has the name of a service bus, where the mortgage codes are to be sent. You might say, oh, this is really nice. I can see now I'm creating banks like there's no tomorrow. What does it take to implement a method like this? Well, the great news is that with CDK, the implementation of this createBank
method is literally a one liner, while it's a four liner here on the slide, because there are some line breaks. But in the end, it is a very, very simple translation. And that's the power of object-oriented languages. You can make your own abstractions, not just for your application code, but you can also make your own abstractions for your deployment and configuration. And if you look back at the picture that we had of the four levels of automation, we have mentioned that the bottom one, the deployment we don't really need all the provisioning, sorry, we don't
need because they're serverless, right? There's nothing to provision. You see that all of the parts are here. Like we have the name of our deployment artifact, our Lambda function that we've got to use. We have the configuration that we're gonna need for this bank, and we have the composition. We tell the banks to send the responses to a event bus, and let's call it the mortgageQuoteBus. And we actually implement that with lamda destination, as you can see down at the last line of the implementation of the createBank method. So you can see that our infrastructure
automation code, I almost don't want to call it infrastructure, our automation code starts to look like application code. And that might remind you of what I said in the very beginning that with modern applications, integration and automation application code starts to blur. And I consider that a very, very good thing. So serverless automation isn't about provisioning. We do that for you anyway, but it's about composition and configuration. And remember, composition is essentially integration. And that's when I say are you building serverless applications, right? Think about integration because that's what the composition is all about. Now
let's take this a level further. Remember we have the patterns. So we talked about banks, but we haven't talked about our patterns yet. So CDK actually has something called a construct library. We have low level concepts in CDK, like I need the Lambda function or an S3 bucket, very basic things. And then we have higher level constructs, right? I need an API gateway that talks to a Lambda function. I need two Lambda functions that talk to each other with an EventBridge. If you're starting to think like, ooh, these kind of start to look like these
design patterns, you're actually onto something. The thing that these design patterns add is they give these things a proper language. So you don't need to say, well, I need two Lambdas connected by an EventBridge, but rather you can have a better term for that. You can say, ah, I need a recipient list. That's the vocabulary that the patterns give us. So let's test this on our loan broker. Now we take the part of the loan broker, where the banks send their responses. As I mentioned, they use Lambda destinations to send the responses. Now, unfortunately, if
you use Lambda destinations to send events to SQS, the events get a lot of extra packaging. You have the whole event wrapper. And that's useful in many cases, but it's not useful for our loan broker. So we want a content filter. We want to reduce the amount of data in the message because it makes no sense to lag around all this unnecessary stuff. Just hurts out performance and probably in the end we end up making mistakes because we have too many fields that we're looking at. Also, some banks will send empty responses because you need
to send a response from the Lambda function, either on success or failure, but you cannot withhold. You have to send something. So our banks just send empty messages, which we don't really like, they're not very useful. So in addition to the content filter, we also have a message filter. The message filter sorts out empty responses, and just doesn't put them on the SQS queue. Now let's see how we can use our CDK abstractions to make these patterns more expressive. We wanna use the language of these patterns. We want to have a content filter and a
message filter, right? And here again, the description of what these patterns really are so you don't have to read the 700-page book, right? Remember the message filter is something that eliminates messages, right? It doesn't pass on all messages. Only those that match certain criteria, right? Can also be done with EventBridge. And we have a content filter, content filter doesn't drop messages, but it drops fields, like a big message goes in and a small message goes out. And we found that this is a special case of a translator. And we found that EventBridge also can do
translation. So you wouldn't be surprised that now we can write code that maps this on to EventBridge. But since we're working in an object-oriented language, we can do this in a very clever way. So we have a message filter. It's the non-empty quotes only message filter. And that message filter has a predicate in it. It says the response payload has to have a bank ID. That's those, the predicate language that we use. And we can use that in EventBridge. And there's no noise here or anything. It's very clear that that's what we're doing. So our
code looks equally clear as the design pattern. This is a message filter and the defining parameter for message filter is the predicate, like, what are you filtering by? Which messages are you gonna let through? And which messages are you gonna drop? And then for the payload filter, you can do something similar, right? We have a content filter where we're filtering out the payload and we'll see in a second, how we define which fields we keep and which fields we don't keep. In this case, we were a little bit extra clever because omitting everything but the
payload is so common that we actually made a special subclass of the content filter. We made a content filter, which always just passed this on the payload and drops all other fields. And now you can see how we can combine these patterns and do the composition. So you have the sourceEventBus. You have the targetQueue, right? That's where the mortgage quotes go is the mortgageQuotesQueue. And we use the message filter and the content filter we defined above. Now, if we wanna double click on this and zoom in one level, we can see how these components are
built. And the amazing thing is these abstractions are very, very useful, but they're not terribly complicated. So here is a message filter. And underneath you will see the matching CDK constructs. You have an event pattern. You can see that this pulls out of the event bus, right? And the event bus with EventBridge supports an event pattern. And that's in the end of the pattern we define. So the translation isn't horribly difficult. But all this noise, we don't need in our automation call. We can just say give me a message filter. Well, we do the same
thing with a content filter. Again, here is the predicate, right? This is the code that we saw previously. And we can see how this is implemented. Do you see down there? I said, we made a special payload filter because this is a common requirement. We see the JSON path, right? And it's not surprising, right? It's the root of the detail and the response payload. But we don't have to remember that. You don't have to remember the syntax of the little dollar sign and that it's wrapped into a detail element. And then inside the detail element,
we have a response payload element. Well, we can just write the code and say, please filter me the payload out of this event. I only wanna see the payload. I don't need all the extra decoration. And when you say to people that, oh, I have a content filter for the payload. Everybody will exactly know what you mean. And it's very easy to implement this in EventBridge, but just imagine your filter is complicated. Let's say you need maybe something where if this or this field and that field, or maybe this other field, that certain values, right?
If this becomes very complicated, you can replace the EventBridge with a Lambda function. You just write a Lambda function that implements a more powerful content filter and your code would hardly have to change because you abstracted the implementation behind the pattern. But again, this is what I promise that here comes serverless automation and integration and patterns to come together. And it gives you awesome capability, right? And what we see last so far, the two pieces of code really set up the two data structures, so now what we do is we map this into the EventBridge,
right? And you see there, we have an event rule, right? That's how EventBridge works. For one bus, you have multiple rules. For each rule, you have an event pattern. Careful, this is not an integration pattern. This is a pattern that defines which messages are gonna be processed. It's the message filter. And then we add the target publishing to queue. And you can see in the very last line that our content filter actually becomes part of the target. It isn't really an independent content filter. It belongs to the target. But again, we didn't have to know
that because that is absorbed in the bottom half of this code. And in the top half, we see very, very little about EventBridge. We are in our integration patterns domain, we see message filters, content filters. We see predicates, we see event buses, we see message queues. So with just a few lines of translation code, we are able to describe our solution at a higher level of abstraction. And that's the level of abstraction of the integration patterns. And the amazing thing is this is all executable CDK code. This will, of course, you know, deploy and run
and wire together, new solution. This is totally executable. So when we look at building this, so actually when you have my friend Lewis who worked with me on this, when we built this, one thing we had to think about is how do we express these patterns? Like, what's the OO structure behind this? And this is something they're actively working on. You could call this domain driven design for serverless integration solutions, right? What do you express? What is the rule? What is an event pattern in CDK? But how do you translate that into your desired domain,
into integration patterns? Because you see at the lower level that actually the content filter was part of the rule, but ideally we wanna consider that something separate. You might want to content filter. You might not want to content filter. So what we're currently working on, and I would share the link to the repository is to see how far we wanna go from the actual implementation. Because the further we can move away, the better our abstraction works, but also the more translation we have to do. So in this case, we choose to do a little bit
translation. The proper term for that would be a gateway pattern. We have an existing implementation, but we want to interact with it in a slightly different way. We want to choose different abstractions. We consider the content filter separate from the target. So we make a gateway that does the translation for us. And the amazing thing in the end, this is like two or three lines of code because we have the power of object-oriented languages with CDK. Now, one question that you might have is does your pattern domain, our abstraction, still depend on the CDK construct?
And the short answer is, at this time, it still does. because we didn't want to recreate everything that's already in CDK that describes the AWS services. We didn't want to have to recreate that. Well, it's an interesting design decision to make. Do you want something that's totally abstracted but then you have a lot of translation work? Or is it okay if some of the lower level concepts you might say leak are to be found at the higher level? For us, that actually seemed to be a valid trade off. We're much more interested in expanding this
to more patterns than, you know, trying to make this so abstract that you don't even recognize what's underneath. I think it's totally fine to know that this runs on EventBridge, but it's great to have this pattern language. Now this is work in progress. So obviously we can take this a step further. We might be able to make fluid APIs. So one fundamental pattern is the pipes and filters pattern. That does much more than just chain these patterns together. So I have a content filter and a message filter, right? That's already an example of pipes and
filters. And what you can do is you could build fluid APIs. They can say, oh, I have a new pipe. And I have attached that to a Lambda function. The implementation would be a Lambda destination. And to this pipe, I append a message filter and here is the predicate, only lead messages through that half a valid bank ID. And then to that append a content filter, right? Don't give me the whole event. Just give me the payload. Then publish this to a queue and then please go generate the runtime. And then the system behind could
figure out that actually Lambda destinations and EventBridge are a sufficient set to implement all the patterns that I mentioned here. If I need another pattern in addition to this, I might need another EventBridge, I might need to build a Lambda function. So this is actually something we're thinking about. We think this is actually quite buildable and you can see how this brings your, I'm not sure what to call this application development or deployment or automation integration, it's really all of it, how it brings this to a completely different level. Now, I think that is something
pretty exciting. So are we deploying, configuring, or programming or integrating? Well, the short answer is we're doing all of it. We defined a domain-specific language for loosely-coupled distributed systems. And that's what all our modern cloud apps are. We use the patterns as the language and we can wire the patterns together with the pipes and filters. And now the amazing thing, this is not a language on paper, this DSL is executable and it's mapped to CDK because we use the power of the object-oriented construct. So we are coding serverless solutions in a domain language, but we
are not executing application logic. We are actually deploying runtime. We're deploying a distributed serverless system, and that is amazing. And it's absolutely something that we could have never even dreamt off if we didn't have the cloud, if we didn't have serverless, if we didn't have automation, and if we didn't have the integration patterns from 2003. So this is my last message for you, right? That automation done right also isn't an afterthought. Some people think I built a solution, maybe I should automate it. You will be missing 90% of the benefit of automation. Done right, the
automation impacts your architecture choices and it blurs the lines between building and running. And it means you can make finer grain solutions and implement these patterns because you have this level of automation. Now this is work in progress. So there's some things we still need to think about, right? Maybe we want to clean up some of these runtime dependencies. Maybe the mapping from parents to services isn't quite as one-on-one. I did happen to be that content filter and message filter is a good match for EventBridge. Not all patterns work as easily. So this is something
that we can work on. And can we do something that does this automatically? So if it sees a message filter and a content filter that says, oh, perfect, that fits up the EventBridge. But then for some other parents, I need to choose a custom component, right? Those are the things we would like to do. What I leave you with for today is to remind you that AWS serverless and integration patterns, and CDK is really an amazing way to build modern cloud applications. And so you can follow along with our work. Here's a QR code and
a link to serverless land, where we're sharing the slides, the blog post, and the code that is behind here. So hopefully I was able to give you a whole new perspective on integration. It's not about enterprise service buses or ETL from 40 years ago. It is an integral part of building modern cloud applications. So the next time you think about building modern cloud apps, you should think integration. Thank you very much. (upbeat music)