 Okay. Hi. I am really excited to be here, and I really appreciate Singapore user group organizers and the crews and everyone in this auditorium for having me today. I'm not sure how many of you here have experienced serverless development, but if you have ever tried developing large-scale systems with serverless architecture, I guess you have probably faced with this topic that I'm going to talk about today, complexity. Today I will talk about how to simplify design complexities using serverless architecture on AWS based on my experiences with using the new services that have been recently announced. Before getting started, I'd like to introduce myself just for a moment. My name is Sonu Kim from South Korea. I was born and raised in South Korea, but I started at university in Japan. I started my career as software developer in Japan, Tokyo, and currently I am working in serverless focused tech consulting company. I have personally liked or admire Singapore. I like the diversity food and people in Singapore, especially as for the diversity, I've never seen it like Singapore in my entire life. I just live in Japan and Korea, so it is very new to me. I've been here in Singapore twice before for a trip, and every time I visit Singapore, I met a lot of awesome people and had good memories. So I thought the next time I visit Singapore, I hope to have a chance for something related to my work. But the COVID-19 situation has not allowed me to do it for almost like three years. Now I am very happy to be here in Singapore again for the AWS community event, which is my favorite part of my job. As for my background, I have been working as a full stack AWS serverless engineer in Japan for several years. I have experienced the project, especially in developing IoT platforms with fully serverless architecture on AWS and web services and mobile applications for those IoT products. During the corona situation, I turned over my career to attack start-up. Now I am supporting several of that teams as my customer and contributing serverless communities and serverless development ecosystem. Okay, let's get down to the main topic today. Today's session has three main topics on the screen, and also please check the items listed in the key takeaways for the specific information to be given in this session. So the first agenda is why does complex serverless design need to be simplified? What is the complex serverless design and why is that a problem? Let's make this clear. As you can see, here is a sample of serverless system diagram. Some of you might think that it is just a typical serverless backend and it looks so familiar and easy to understand most part of the system. I guess you're just like a serverless engineer like me. On the other hand, some of you might think this seems too complicated to understand most part of the system because it is too specific with a lot of duplicated services. Well, one thing all of you might agree is that it is difficult to grasp the big picture of this system at a glance compared to the monolithic architecture. So as you see, it is hard to know what this system is like, so I grip them according to the process they do. Then we can see several microservices divided into the business domain, system features, and so on. Well, also you can see a lot of asynchronous events passing between each services. This architecture may be good way while the system is small. We can even say it is a sophisticated design in the context of serverless architecture. But what if this is a platform for a service with tens of thousands of users? If there is a problem in the system, how to investigate? If there is a missing property in the middle of asynchronous process, how do you trace and fix it? If you want to make a quick release for a promotion like Before Christmas or Chinese New Year, how do you quickly add features and deploy them without any downtime? In addition, dividing into microservices is a good practice. We all know that, for example, it is hard to manage all the resources with a single confirmation stack. However, you will have to make dependencies between microservices because of some reasons that make sense at that moment. Soon after it is going to be difficult to keep the microservices completely stateless, including deployment ordering and so on. For example, when you need to update your entire system, you have to deploy off-service first, then deploy IoT service, then deploy device API service, and finally, you are able to deploy the product service. And I think this is quite a common problem that occurs quite a lot in the real world. So you may face these pain points by this situation. Loss of agility and design flexibility, which is the key benefits of serverless. For example, you will see that it is not easy to identify the scope of impact when adding new features or refactoring. Difficulty in troubleshooting and user increase. Let's say a user has some sort of issue, have to trace the logs of the user request and find the cost that matters. So how do you keep traceability in a long sequence, including asynchronous processes? Degraded testability and degraded developer experience. You can use local stack or packed for asynchronous testing. But does that provide you with a good developer experience? Of course, it depends on the situation. But I would say setting up and testing AWS account and conducting end-to-end testing might be a better choice for large workload on serverless. It should be difficult to run everything in local development environments that connect to degrading developer experience. Okay. So why do we need to keep serverless and reduce complexity to solve those pain points? First thing, from a general perspective, it is one of the essential processes to streamline your business logic in serverless context. Excellent serverless design can be used as a map of business logic. There may be an efficient business logic that degrades the benefits of serverless. Also, still, we have no worries about managing the servers or containers. There's well-known term undifferentiated heavy lifting, and serverless is the best way to offload that works must be done, which is not directly related to your business value. If you choose serverless, you should focus on your application logic and secure your team's energy and time on further business requirements. So what can you do to simplify the complex serverless design? The first thing I would like to note is to arrange unnecessary Lambda functions and triggers. I understand the convenience of implementing Lambda functions for various use cases. However, just like a program source code needs refactoring sometimes, I suggest to consider reorganizing your Lambda functions in other services with Amazon EventBridge. An effective way to reduce unnecessary Lambda functions and triggers is using EventBridgePipes, which was announced recently at ReInvent 2022. EventBridgePipes connects and event sources to targets with data filtering and mapping tools. So you don't need to write integration code or implement ETL process yourself. For example, there is a practice to use a Lambda function for reach rise or error handling when a step function is called fails from SQS. It sounds that Lambda function is not for business logic, so I wonder if it is really necessary. Now you can replace that Lambda function by applying automatic reach rise in case of errors or setting optional dead letter queue as the built-in feature of EventBridgePipes. Also, in the case you want to pass data coming from DynamoDB streams to other services, you need to implement Lambda function to log data or just pass data to the other service. With EventBridgePipes, you can send the records directly to various services such as CloudWatch logs, Kinesis, step functions, and so on. This means that you are able to replace even the microservices with a single EventBridgePipe. Also, there is one of the new features that is announced last year, the EventBridge scheduler. Let's say you have to design a feature that sends mobile notifications to users. Note that if you send notifications to all your users, the user will tap the notification and open the app all at once. You will get a lot of traffic and your workload might burst or be throttled. So one of the effective practices is to split the sending requests with time intervals. In that case, you can make a delay using step functions or using the SQS delay queue. As you see on the left side, you can do it by step functions workflow like this or design Lambda functions with SQS to implement the time interval. However, now the components for setting the time interval can be replaced by using flexible time window of the EventBridge scheduler. If you set the value as 15 minutes, the task will be triggered within 15 minutes. We don't know when it's going to be triggered exactly, but at least we can make time intervals without implementing it ourselves. If you off the flexible time window, then the task will be triggered exactly on time. And I think this could be also a good solution for several specific use cases. There is also a useful EventBridge feature, API destination. This API destination not only allows asynchronous calls to external API and points, but also allows retries, stat letter keys and rate limits to be set so we can eliminate the need to implement these processes. It can be configured as the target of EventBridge pipes. So in the case of combining with EventBridge pipes, there may be some cases you can remove the Lambda function just for calling external APIs. Next, I'm going to talk about step functions, which is popular for serverless workloads. Let's say you had to design a workload, a transactional ordering process for e-commerce, just like the diagram on the slide. I saw this kind of workflow many times, and I think there are pain points like this. There are multiple nested workflows for error handling and recovering process, which is not directly related to your own business logic, like handling system errors from external system. And there are lots of conditional branches depending on success and failure status. In the end, once the workflow is set, it is not easy to be changed. So one of the good solutions to replace transactional workflow is the event-driven approach. I suggest considering a three-factor app design for the transactional process like the workflow that I showed in the previous slide. The client makes a connection to the GraphQL server and gets ready to subscribe to the events. When the client sends order requests to the GraphQL server, in this diagram, it's app sync. It writes an order record to DynamoDB. Then, new order record is processed by each microservices, depends on its status. For example, first, on ordering status, the first lambda will check if the stock is available, then update the status to stock valid. Then on stock valid status, the order service lambda is triggered and handle the order process. Lastly, on order valid status, the payment service lambda is triggered and handle the payment process. So with payment-approved status, all ordering sequence is fine. It works well with the service. It's good to keep loose coupling between microservices. I just want to note that this is not a perfect solution for every case because it requires many new learning costs for example like GraphQL and message handling on front-end. But if your team is full-sac, please take this into consideration for your system core design. When you are working on three-factor app or any other event-driven workloads, we will definitely need some kind of tracing method to debug or trace a single long sequence containing a lot of asynchronous processes. Basically, you can primarily consider utilizing AWS X-ray. However, you should consider cross-account tracing in the case of multi-account backend. Also, you still need a method to record asynchronous event application. In that case, you have to inject a tracing ID to a synchronous event context. It will help you when you trace a long sequence including asynchronous processes. I suggest having or considering this kind of practice to handle like tracing ID or correlation ID. Another basic thing you should do when tracing is to be able to produce structured JSON logs. You can use tracing ID or request ID to list the logs of a series of processes in a sequential manner. There are useful libraries for JSON logging, such as adepts lambda power tools for typescript or Python. Here is another suggestion for reducing complexity. Next, JS, which is a server-side rendering framework for ReactJS, and NoxJS, which is also a server-side rendering framework for ViewJS. Both of them support lambda as their build target. That means you don't need to use Fargate or AdMuner just for provisioning server-side rendering applications anymore. Unless you need to configure a static outbind IP address, you can remove network configurations on your system diagram. I'm afraid to say you don't need VPC just for provisioning server-side rendering apps anymore. Next, consider bundling API logic as a single lambda function. Combining API gateway lambda functions in DynamoDB is a very famous pattern for serverless RESTful API. Let me note this at first. Serverless BAS practice is single-purpose functions, so a lambda function should keep its logic simple and small with clearly defined scope and responsibility. However, if you create a lambda function for each and every API operation, you will have to create many lambda functions with almost the same boilerplate for API logic. When you have to create a single use case with multiple API calls, you have to collect all logs from different log streams. Also, lots of lambda functions, lots of cold starts. I would say for web API logic, creating one lambda function makes better sense. Well, some of you might be concerned that I am using lambda functions for too much work, and it is bad practice. Basically, I agree with that. However, in context of web API design, I'm afraid to say that it is actually more efficient and clean with those libraries on the slide. If you are using Node.js, there is the lambda API. At every serverless hero, Jeremy Daley created this with Express-friendly API routing interfaces. If you are using Python, you can use AWS Challenges with API routing decorators. If you are using Java, there is Power Jamda with API routing annotations. I personally released many products on my work with lambda API and Power Jamda with a single lambda function for API logic, and never made a problem yet. Let's go on to the last part of our agenda. Architectural approaches with Amazon EventBridge. I spoke some features of Amazon EventBridge. I suggest reorganizing microservices with using Amazon EventBridge. You might think that isn't it a single point of failure? Is that too dependent on one service? I would like to introduce some points of view to consider when dealing with those questions. At first, you can use EventBridge with a centralized design. It is easy to integrate applications or microservices with minimal changes. You can just add a rule with an event filter and send the events to the rule. If you are using multiple AWS accounts, you can use a single AWS account as an event passing dedicated accounts. However, as you can see, if the EventBridge in a certain region stops for a while, it can be a single point of failure. Also, here is another example. There is no concern, single point of failure, and each service has its event bus to communicate with other event buses. It is a distributed pattern. It's actually good for multiple DevOps teams to collaborate on large-scale development and keep their services autonomy. If the EventBridge in a certain region fails for a while, you can route the event to another region in the same database account. So you can minimize the effects of the failure. However, it does not help with reducing complexity. Here is a summary of each pattern, centralized and distributed pattern. I would not mention that which one is better or which one is, well, good in some cases, because it depends on the situation. However, you can refer these patterns to optimize some part of your system that can be improved. The diagrams are actually based on the situation in 2020, but now there are some more features for a failover in the EventBridge. So please check that if you have to care the downtime, well, there are solutions you can use for region failure. I didn't mention it in this presentation because it's, well, quite out of the topic, but there is some kind of method to do it. That's it. Thank you very much for listening and attending this session, and please feel free to ask me any questions about the content. And lastly, I just came from Japan, and I guess it was quite out of blue for you both. If you are interested in me, my work, or doing or expanding your business in Japan, please check my company website and contact us. That's all. Thank you very much. Ladies and gentlemen, do you have any questions for... So no, we have time for one or two questions. Maybe you can ask...