字幕列表 影片播放 列印英文字幕 Decoupling Applications from Architectures - Jeff Hoffer Hello, all right. Thank you everybody for coming. My name is Jeff Hoffer, and my talk is decoupling applications from architecture. I gave a talk on this side last year and there was literally 10% of the people in the room that there is now, so thank you all for showing up. I really appreciate it. The lights are really bright so I can't see anybody's face, which is probably a good thing. All right. So a little bit more about me. I've been professionally developing software for 20 years, and I recently joined a company called bluebeam. It's for the design bid and build phases of projects. You can find me on GitHub at @eudaimos, it's a word I made up, kind of. It was inspired by Aristotle's eudaiminea. The inspiration is my son Eric who's here with my wife this week, if you see us around, he's nine now, please say hi. So it's appropriate that I work in a software company for construction. I notice that as human beings, we've been in the practice of building things for a long time. We built some amazing things that have stood the test of time, beyond which most of us can even conceptualize the length of that time. And with software we've come up with a single-most malleable thing which we've ever had to build anything, and it's been pretty great. But still there's something not quite right. When I worked in Santa Monica, there was a lot of construction nearby all the time, and as we were walking our VP of Engineering, my boss used to point out how quickly they could build these buildings versus how long it took us to deliver software. [laughter] So how do we keep ending up here? [laughter] We either build it too slow, we build it wrong, or both. And how does that happen? We get asks from people who don't really know the difference between building application and building it well. But they want to make sure that we don't build it well, sometimes. That' d be great. So we end up in this Kobayashi Maru scenario and for the younger folks out there who haven't seen Star Trek II. It's designed to be a no-win scenario. It's there to determine how we will respond, and unfortunately most of our responses are this. [laughter] Where the needs of the many outweigh the needs of the few, right? And then we get back to our brittle code. So what do I mean exactly by application versus architecture? So we should probably define this. And we can take a step and define it based on who defines each for our software. Product team will define what the application does, engineers will build it, QA validate. Engineers work with software architects to define the needs. They build the architecture, QA validates nonfunctional constraints. So I'd like to take this and define these as layers. So we have an application we call that the development layer and architecture is the architecture layer. And if we can organize these into layers, then we can separate them like what Docker was able to do for apps in DevOps. There's also four aspects of software. Taking another view here, what an application represents to a user, how it does its job, where it executes, and when it does what it's designed to do. Unfortunately, when working between the development and architecture layers, we commingle these four aspects into a kind of a mess. So let's go back to our layer definition and define them using these four aspects. If we say the development layer is the what and the when, and the architecture layer is how and where. We can organize them this way and if we can organize them this way, along these layers, and make them orthogonal to each other, then we have the decoupling we're looking for. So if we take these aspects and graft them on to our layers, the development layer has what the context of the application is for a user, when does a user go from one context to another. architecturally how will the app move from context to context and where will the code reacting to this actually execute? So if we get into the details of that so that it actually matters to us who have to build this stuff, we can borrow some concepts from computer science where we say the what these contexts are signals, when is triggering and the reaction to these signals. And the how is -- and the where is a signal network and the implementation of where that signal network will run. So I want to introduce a few things for the demo first. There's RealWorld. If you haven't gone to realworld.io, it's beyond a standard to do app. It involves conduit which is a Medium-like blog platform and there's lots of concepts which are integrating different front ends and different back ends all doing basically the same functionality as this Conduit app. I also wanted to introduce TAO.js. There's a project that I've been working on as a solution for this. It has a meta language for defining signals that end up in the code, using trigrams. It provides interaction primitives to determine when to interact with these signals and it implements signal networks. So a trigram, the library TAO actually comes from term action orient, so we want to describe these contexts with these three dimensions. So term is something, action is an action taking place, and orientation -- orient is a perspective. Three ways to interact and respond to signals is inline, do them in order. Async, do it in parallel as a side effect. And intercept is do it first and optionally you can interrupt the flow. And implementing signal networks, it abstracts that away from the application code, and it also has the implements a signal chaining, so that if you are reacting to a signal from the network, you can provide it with another signal that will chain itself, so this gives us very small bits of code that we can implement now as it -- using the signal network as the medium for the interaction. So taking this back to our notion of aspects to layers and now to TAO.js, what we're able to do with the development layer using TAO.js is the powers that engineers use to define these trigrams and define the desired behaviors when these are signaled in the signal network. The engineers implement these behaviors in the code using these interaction primitives on the signal network. And the architectural layer, we allow the architect or whoever's implementing the architecture to choose the signal network, and implement using that, and decide where that signal network will run, so we've now abstracted away where the code has to run in order to interact and provide the development layer with what it needs. A quick note about trigrams versus an application context, so think of a trigram, it's a 3-dimensional signal, also an event. You can subscribe to trigrams on the signal network and it's like a class in an OOP where ar application context would be an object in OOP, and so it's a specific context and so it combines this trigram with the actual data associated with the event that's happening the signal. So when going through the real world conduit application, what we'll do first is define the terms. We have an app, user, article, comment. We'll define some actions, init, load, find, exit. We'll define the orientation so we have a portal and an anonymous orientation, so portal is when you're logged in. Anonymous is when you're not. And then we'll define some paths. This allows us to understand how to react to signals on the network, and then throwback new signals in order to keep this chain going. And this is why we also develop or define very granular actions, so that we can have more granular chains and we can hook into these at any time. So I'm going to go into the demo portion. OK. So we're going to start with our Conduit app. And -- of course! [laughter] OK. Oh, I understand why. All right. Um, so this is working with their -- with the default API, which is using a service somewhere. And we're going to -- oops, wrong one. Sorry. Of course this all worked in the room. (Whispers) now completely broken. So I'm going to skip showing it as a ... because it doesn't seem to want to behave with Redux. So it was a Redux React based application. That still doesn't want to play nice. Very sorry for this. AUDIENCE: You got it! >> Thank you. I can't see who you are. But thank you. All right, let's just turn all of it on. Anybody know which screen I'm on? I can't imagine what changed. Oh, I, yeah, now I know. This is the best! OK. [applause] Getting the applause for code you didn't write, it's, thank you! [laughter] So this is Conduit. This is the basic React Redux example I think probably the first one they did. So we're going to modify this, time willing. To use TAO.js. So this is actually hitting the API for that, we can sign in, go to home page, so you see there's all this functionality. There's lots of ... so this application has a bunch of reducers that we're used to, all the Redux boilerplating, the components are React, they have a lot of baked-in Redux, so we've sort of embedded all of this together, what these -- how we call the API, what to do on various -- within various components. So if we go ahead and -- I'm going to turn off Redux logging. We're going to switch to using some alternative components and not all of them are necessary. So we notice, still looks the same. Still works. I didn't get that tab thing working yet. We see the results change. And the way this is implemented is -- so for the home, the original home is here. It's home page loaded, home page unloaded, applying the tag filter, we now take and add a handler. We import a TAO signal network that is the default signal network, and in order to chain a signal into the network from a handler, we use this app context constructor to return that as a new signal to follow, and so when we receive the signal to enter home, we want to tell the application to view home. Alternatively, we use a data handler from the React library to capture data from a signal, and store it where we can utilize it again in our render handler, and this -- you provide it with a context to say any data handlers above, they create this context for me, I will now have these available to me in my handler that I use. It's a function as a child component. And so when it -- this will react when we reach the trigram -- in the application context that reaches the trigram of home and view and we actually don't care about the orientation. So it allows for wild-carding, so if you don't provide a term, it's a wild card on that term -- on that attribute of the trigram. And so when we render the home, we actually want to use our home component. This one's new. We pass down the token, the app name, we get from the app data, which tabs are active, a tag, and then what to do if they click "All" or click on the feed. So this is a -- just a standard React component. There's no logic or anything in here to interact with the signal layer, signal network and we can treat our components completely on their own, makes them easier to test, easier to design using storybook, and this works for many things, so this uses a main view. Vue, and you notice that the interactions are -- there you go. We pass down this "Click all" which will then -- this will create a signal into the network. Actually, this is not implemented right. So going in here and signing in, I come back here and see the signed-in view and we did that with using a different header. Nope, wrong window again. So we have our login component, which is saying if I'm trying to view -- if I'm trying to enter a user anonymously, I want to view that user anonymously, so that's going to render -- use a render handler, user view, anonymous, and that will give us our login control. When we submit, it's going to generate a user find action anonymously. Because this is just working with what's already here, we have these handlers all defined around users. So we find a user anonymously, we have a handler for that, which will grab the email and password out of the data -- out of the user portion of the data passed in the application context to our handler, we will leverage the agent and do a login, and then based on how that responds, we'll either get a user or it will catch an error. If we get a user, then we want to enter that user in the portal orientation, and provide the token so that that can be used. So what's nice about the ability to have these wild card handlers, when we pass the token and we're in the portal orientation for any of our signals, we make sure to set the token on the agent every time based on that. So we don't have to call this anywhere else, but it will always be accurate. If we are going anonymous, we change -- we set the token to empty. We also can react to initializing the -- so our initial application context will be appinit anon, we can grab the token from local storage. If there is a token, we'll switch to the portal view. If there's not, intercept sends nothing back, it will chain to the next appinit anon. That's when it will call load. So using this, we now can refactor all of the application logic into these handlers that are small and bite-size and easy to change, and we don't have to worry about how we're making calls necessarily to move the user around the application. On the -- not sure how much time I have left. So going into the API side -- so if I switch this to using the local API -- let me sign out first. Clears the token. And we also see, through logging these, that these chain to each other, -- now, let's ... We'll use the local API. Now for this, with the same user, we set up -- we convert these routes from the API where instead of doing what it was -- what it had here, gets the user find by, it's going directly against mongo, we can instead create a transponder on our signal network and what a transponder does is just provide us with a promise when we set the context that we can await.