 So, in this demo, it's going to be moving from a client to a server to microservices and eventually to RIF. I'm sure a lot of people came here to see RIF. This is from the developer's perspective, and the point of this is that if you can write a good function, then you can write a good function as a service. So we'll get to the function as a service at the end. All right. This is the demo app, okay? It's called Bolarama. It is an example of the bowling kata that got a little bit carried away. So this is for if you are at a bowling alley and it closes down, right, before you're able to finish your game, you can go home and you can finish the game, okay? So make a new one, and this is your score from the bowling alley, right? And then you can keep rolling, okay? These are all going to be valid rolls based on what was rolled in the last frame or in the last roll. It also validates, so if you have a bad game, right, and you go zero, zero, negative one, that is not allowed, right? And you can't have more than 10 in a frame. You can't have more than 10, and you can't have more than 10 in a roll, okay? So but you can have a regular game, okay? And then you can save one of these games, okay? And we can also load games that are already in the system. So if I load all the games, then the one that I just saved is right here, and I can continue with it, okay? All right. So this is what we have, and everything is working very well, but things will change. So this is running with ClosureScript, I have a REPL where I can run my tests. If I say, I apologize to anyone that can't see this, I'm going to try my best. So here are the tests for scoring, right? And you can see negative pins, more than 10 pins, so validation and scoring are being tested together because they're being used together, right? When I try to get the score for a set of rolls, I either get a success with a number or I get a failure with one or more errors, right? And I represent that duality with the either monad that allows me to hand off the result of that calculation and defer making a decision on it until the time when I'm ready to make a decision, which in this case is HTML, okay? If you look at the code, we have that package, the scoring package is where all the interesting stuff is happening. These are the tests that you just saw, and this is my testing framework. It's just a quality comparison. If you are using pure functions, all you ever need to do is compare return values. It's testing some failure cases, testing some happy paths. This is the function that is being tested. I jammed it all into one function that isn't all that readable, but you can still sort of see what it's doing at the bottom here. And before actually running the score, we validate the game. That returns an either as well, right? Which can be flat mapped with this other function. So in the end, you wind up with success or failure, and that can be handed off until someone is ready to make a decision about it. So we realize that everything's going well. We're starting to get competition in the unfinished bowling game market, and someone in the business department decided that this algorithm is our competitive advantage, and we can't be shipping it to the browser, so we need to move it to the server. The way we're going to do that is just by moving the files. So this is a package that runs on the browser. This is a package that will make a jar, and we can deploy that jar on the JVM. So this is ClosureScript, but we really want Closure. I'm not moving this either because we need that representation of success and failure on both the server and the client. The idea is that we're going to represent the result of our scoring with that either, and it needs to be passed along until the point where we're ready to make a decision about it. So we're moving from the server to the client, and then turn it into HTML. So not moving that one, but moving these other ones, and we need to turn them into Closure. So don't do that. I was hoping to be able to avoid the mirroring, so you didn't have to see all my notes, but you're going to have to see them anyway. So let's go into that package where we just moved those files, and we have ClosureScript files. We want Closure files, and we're also going to want that either. We're going to want that either on both sides, and we're just going to use a stem link. Closure and ClosureScript play nicely together. That's why I chose them. Other languages can do that as well. One is a good example. So here, same syntax, it'll run on the JVM, it'll run in JavaScript. Now I have my either on both sides, and I have these Closure files for scoring. There's one little bit of syntax that is unfortunately not the same, and that's this. So we'll just replace those, all files, and we want to be able to run the tests on the server side as well. So here's Liningan, this is like Maven, but better, and has a REPL. So now we have a task for our tests, and we get the exact same tests. Now we're running them in Closure, instead of running them with ClosureScript. Same function, same validation, same test, now we just moved it. So we have this scoring package. How can we connect it back to the client? So we have a controller here with all of our endpoints. This is Spring and Kotlin, and this needs beans. So I have a separate configuration. I have externalized configuration to turn Closure into beans. And yeah, good luck doing it any other way. So this requires a Kotlin interface for any of these beans. I have all my interfaces and use cases over here in this bowling package. We'll make a new interface, call it Scorer, and we'll steal the implementation from over here. And now we want, shouldn't call this an implementation, steal the code from over here. So now we want an implementation of that interface. And unfortunately, there's still a little bit of friction between Closure and Kotlin. So I'm just going to put in some boilerplate, if I can find the closure namespace. Okay, I'm going to put in some boilerplate that I'm not happy about, but it's not really the focus of this talk, right? This is just converting between Closure data structures and Kotlin data structures. So this function will return an instance of Scorer, right? And it has that Scorer method that takes roles. Now we want that as a bean, so we're going to need to depend on that package and try not to mess this file up. We'll figure itself out. Nope, it won't. Okay. I don't know why it keeps jumping around. So depending on that, and in the configuration here, I'm just going to take my bean from here, okay, there. Now we have that Scorer bean, and we want to use it in our controller, so we want a new endpoint for scoring, right? Previously, this was done on the client. Now we want this to be done over HTTP. So here we go. The Scorer endpoint, and this requires an instance of Scorer. So do it like that, and we need to configure that. So go over here and say Scorer, pass that in. So now we're creating an instance, and we're exposing an endpoint. We just want to build the new jar for this, okay? And I had set up the palm beforehand, so everything should work with that new scoring module. The other thing that we're going to need to do is jump back to our client side, and now instead of doing that scoring locally, we're going to be doing that scoring remotely. So we need to make an Ajax call, and I have something ready to go here. This is like a cooking show. Have everything prepared ahead of time. So yeah, you see there's saving, there's fetching, and now we have scoring and rolling. So all of the Ajax calls are happening right here in this file. And they're all going through a little node proxy that makes my life easier and just distributes based on the path to different services. So now we have this remote scoring, and we want to call that instead of this local scoring. So we're going to come in here, we're going to say remote score, we're going to get rid of this local scoring. Those files don't exist locally anymore, it does not exist locally, it does not exist locally. So once this is done, we go down into our modulus package, and we see a push. And while that is going, we can go back and talk about why I'm doing all of this. So Cloud Foundry makes it so that developers do not have to think about things like provisioning VMs or firewalls or any of that low level computer stuff. And I'm very grateful for that. As a developer, I like to spend my time thinking about users. And there's a big difference between the things that we do to satisfy users and the things that we do to satisfy computers. This is often called the value line, or Martin Fowler calls it strategy versus utility. You could also call it essential complexity versus accidental complexity. I generally side with the users here. Users don't really care how you run the software. They don't care if it's in the cloud or on your laptop. They don't care if you have Bosch or what your manifests look like. But that stuff is very important. I'm very grateful that Cloud Foundry is around making it so I can spend more of my time thinking about the user's needs and less of my time thinking about the computer's needs. So this demo that I'm doing is to show how if we isolate the stuff that is user-facing from the stuff that is computer-facing, then it's easier to change the stuff that is computer-facing. I'm just refactoring this app a few times to change the deployment strategy without changing how a user interacts with it. Okay, so that's been deployed. We should be able to run this with the new endpoint, and it works. Also, credit to Oliver Gurke for the name Modulet. I don't know if he made it up, but his refactoring to a system of systems talk really inspired me for this talk and try to show an example of moving from a Modulet to microservices and then beyond. So at this point now we have the remote call, and that's great. But now we're realizing that the number of requests going to the rolling and scoring endpoints is about 1,000 times more than the number of requests going to the fetching and saving endpoints. Fetching and saving both depend on persistence. The persistence in this module here is implemented with Datomic. Datomic has a massive peer library that is very chatty, wants to keep up with the rest of the system. So someone from the business department comes back and says, we want to be able to deploy the rolling and scoring endpoints independently so that we can scale them independently so that we don't have that extra overhead. So okay, great. We're going to take the Modulet module and we're going to make three new ones. So we're going a little bit overboard and we're going to make three services here. These are based on a book from 1974 called Composite Structured Design. And by Glenford Meyer. And in that book talks about source transform and sync. Source being where you get data from. Sync being where you save it to and transform being all the interesting things that you do with it. So we're going to keep our pure functions related to transform and then persistence is a syncing source. So the idea being we don't need all these endpoints for every one of these services. This one is the sync, so it's only about saving. So we're going to delete the ones that are not saving endpoints. Delete the dependencies that are not about saving. Go back to our configuration, delete everything except the game saver. Delete everything except the game saver, okay? And do the same thing with these other two, okay? So this one, this is our source, we only want the fetch. Delete everything except for fetching, fetching. I have yet to get through this demo gracefully. I decided it was only because I don't have enough pressure. So hopefully this will help. Okay, so here we only want the score. Roll is just a Kotlin function, so I didn't need a beam for that. And only need the score here and score here. So the point of doing this is that I don't want the dependencies for persistence, right? Now everything is wrapped up in this beans module. So I'm going to need to make a new one that does not have the persistence stuff and only has the scoring beans. So now we have a scoring beans. Do a little rename from beans to scoring beans so that the jar is named correctly, okay? And inside of here, right, no longer need these fetch and save, complaining because it's not included in the parent palm, but that'll work itself out. And we do not want the dependency on persistence from that palm, okay? And in transform, instead of depending on the beans, we're going to depend on scoring beans, okay? And at the bottom we have some shenanigans for profiles, for ports and adapters, style, approach to persistence. We need to delete that, not interesting anymore. And we need to include all of these new things in the parent palm. So now we have a scoring beans and instead of a modulus, one more. We have these three services, source. And while we're at it, let's just delete the modulus, okay? Back on, and we want to install that. So now we'll have duplicated. Someone has modulus. Did I not do this for all of them? Let's do this for all of them. So sync needs to be, rename sync. Source needs to be the source and transform. IntelliJ is trying to upset me by renaming things. So this is our sync, looks happier. Okay, while that is installing, we're going to jump to our, what do you have right there? Remoting, so now instead of the modulus, we want to go to these three services. So this is for saving, that's sync. This is for fetching, that's source. And these two are transformed, spelled correctly, okay? And the node proxy will take care of making sure those go where we want them to go. Okay, this guy is still installing. So give that a second. Right, back to the big ideas. So the user-facing code versus the computer-facing code, and I find this is very helpful usually whenever I look at a line of code, I'll ask myself whether it's a user-facing line of code or a computer-facing line of code, and I want the maximum percentage of user-facing code. One way that we sort of enable these two, one second, now we have our microservices. We want to CF push them. Source, transform and sync. I know you can't see very well but hopefully you know what this is doing, CF push. So yeah, to enable the independent changes to user-facing stuff and the computer-facing stuff, we have this idea of 12-factor app, right? And if you have a 12-factor app then you should be able to run it on any infrastructure, run it in any cloud, not really have to worry about everything underneath that app. Now, this really says, okay, we want the behavior of the software to be a pure function of the source code, right? But it doesn't say anything about keeping state in the app. So you can keep as much awkward state in the app as you want and that's fine as long as you're always deploying the same artifact. Once you start breaking that artifact down and you're deploying bits and pieces of it here and there, then that state becomes troublesome, because it's very hard to break that state apart. So I think with the advent of function as a service we should just be renaming that whole thing and talk about 12-factor functions that are also stateless. So when, the name for the talk was 12-factor function, I felt like I was obligated to come up with at least five and now actually I don't really feel like talking about them at all. But we can say that it should be a pure function, should be no side effects, no side causes, don't ask for more data than you need and don't produce more data than your consumers need. Okay, so if we jump back here, it looks like everything is deployed. Hopefully this still works. So now we have microservices and they work and can we load games? The atomic takes a minute to load up the database the first time you deploy. We can and yeah, our microservices in the middle because it's all alphabetized, future, future. So okay, so we've gone from client to server to microservices and now we're actually about to add a new feature that instead of fetching the games once we actually want to refresh the games every five seconds and we've looked at user behavior and we realized that people only use the unfinished bowling app around like 10 or 11 p.m. And it's absolutely zero activity at 7 a.m. So we ran the numbers and figured out that if we deploy this as a function then the costs will be much lower because the function should hug the curve a little bit more tightly. All right, so I have this module with nothing in it ready to go and we're gonna add a riff function. All right, and again this is pre-prepared. Has all this flux stuff. When I first wrote this I didn't have the flux stuff and it was pointed out that that is going to be an inefficient use of resources. So the flux is similar to the web flux that you might see on Spring Reactor, reactive stack and we in this case will only need to depend on this reactor core and we're also gonna need that scoring function. So we're gonna drop that in here and go back to this guy and get everyone to import. Okay, okay all imported. Now let's rebuild and while that's going we'll jump over to this riff tab and let's see what we wanna show first. So I've already installed riff. Hopefully if you attended Guillermo's talk you understand how riff is working. I'm not gonna get too deep into that but we can see what's there. So we have in this riff system namespace we have Kafka, we have a function control and a topic controller for the control plane and we have HTTP gateway and Kafka for the data plane. So when we invoke our function it goes to the HTTP gateway and the function is invoked by a particular topic. Has that the sidecar and the main app in the pod talking over GRPC. So there you go that's a quick overview of how riff works. And okay this is still going while we're waiting for that let's actually do this new feature. I would normally wait, make sure everything still works with the new setup before doing that additional feature but we don't have that much time. So let's see what is my new feature? I deleted it, that's not good. Okay so this is just a little reframe code that is going to call fetch every five seconds. Okay and we're gonna need in order to make that happen we just need to instead of calling fetch games call refresh games. Okay okay we have bolderama ready to go so we're gonna do this riff create function that is going to give us a docker file and a couple of YAML files. Let's see if we can take a look at and we're gonna call this score. That's not what I wanna call this score. Okay so the input here is that's gonna be the topic that we're listening for. Okay and so this is gonna create that docker file, push the image up to docker hub and then in the YAML for the function we're gonna have a reference to that image right which we can, whoo, does not like my image. You think it's because I accidentally started the other one? All right let's see. Let's do it again but let's just give it a different name here so we don't have to worry if there is something already up there. Let's just call it new, call it scores, see if that's happier. You can see what we have, function, get a little debugging in here and let's see, okay. Creating container, running, not terminating, not error image pull. Hey we got a value, that's awesome. So this is the most graceful that my run-throughs have been. Thanks to Jacques being here. Okay so I did wanna show one thing real quick while we're doing this. If we are fast enough, right, this is creating, we can get the logs. You have 10 seconds, so that's main and so you can get sidecar, okay. You can see messages coming in, 10, 10, 10, message going out, value 60, right. And those messages are going to Kafka, right. Okay and now let's try it from here. Wait I need to change, I flipped that so let's go to our remoting and this is no longer transform score, this is requests scores, because that's the one that works, score does not work, okay. And it will take a second at the beginning to spin that up and then once these requests are coming through, steady stream of requests will keep the function alive, right. That container will stay there and there we go. Okay so every five seconds it refreshes and we save money, okay. That's the whole transition, the last thing I just wanna say, this is my scientific diagram for why pure functions are more mobile and this is the image I usually have in my head because when you have an input output relationship between a caller and the callee, right, then that's an explicit relationship on both sides and it maps very nicely to request response, right. If instead you have this kind of relationship, right, where you have a caller and a callee in some ambient state, right. It's very hard to translate that into a request response relationship, right. If you have this ambient state, what would that be in a remote situation would be like, I don't know, pushing it up to an S3 bucket or something, right and life would be much more complicated or you would just refactor so that it became request response, right. But if everything is always just input output, right, input output request response maps very nicely. Okay, I wanna thank the RIF team for helping me out to do this. I had not used RIF before and they gave me some very helpful guidance. I think the moral of the story for me is you want that sort of independence of your business logic and the most independent you can be is to have a pure function and that pure function should be pure expression of user-facing logic, right. The metaphysical part of this, I think a lot about the idea of state and like the idea of state in the world around us and I think that that's imaginary, right. Like there's nothing on this curtain here that says like it's a hash map, black, true or color black or whatever, right. That stuff doesn't exist. And with pure functions, there's this idea of like point-free syntax with Haskell that you wanna express as much as you can in terms of relationships, in terms of patterns and in terms of functions, right. And not in terms of the parameters to those functions or state. So I think that the function approach is very good. You can see the same idea here with like Gary Bernhardt's functional core imperative shell or with how React works or Elm works or Haskell works, right. There's nothing new in this talk but I just wanted to put it in the context of deploying things in different places. So thank you.