Packages and your data on Umbraco as a service
As Umbraco as a Service becomes more and more popular, there's also a rising expectation for your packages and code to be fully compatible with the service and with deployments between environments.
The task of making your code compatible with Umbraco as a Service can be anything from just a few minor adjustments, to implementing custom resolvers and providers to support data transfer and transformations.
In this session we will give you best practice tips for developing on Umbraco as a Service and talk through some of the common development errors and ways to avoid or resolve them, as well as looking at an actual implementation of a data resolver and a matching provider for custom data stores.
View transcript
Rint farklı desdeues verணamos esta sesión acerca de cambiantes y tus datos sobre Brokers as a Service and de lo que necesitas saber en ser desarrollador, haciendo cosas tanto entre tus cambios para tanto desde pistonos que generalmente con月 a precio hacer para que se pueda activa la cuenta esta sesión se trata de endangeredios sobre Vivo BW-Brandon, y lo que necesitas saber para desarrollar cosas, tanto con vuestros cambios y en general con los datos sobre Brokers as a Service. Sigo utilizando DATABTS ON BRANCS, como forma de ver性 blend from gidoonhub .com.ised, digitalizecosumper connecting centre-level serviceудь mostrando la outtawordetteàn ombriras ochtsakorreести service and whats you need to know as apiej Jeongkgjoevar PROS всей szcz Larsнаutblolj tridands次 ombriger as a service The Psystal auf® retorngšu,ç zol Growing contar 1€ I work on core and Courier. But it's all really interconnected, so you have to kind of know all of it. This is basically what a project on Umbraco's service would look like. You have specific environments. You have a live environment, and you have your local environment. And optionally, you have your development environment also. You could also add in a staging environment. We have not shown that here. The live environment is your actual website. Your local environment is where you would be doing your development on your local machine. Development staging would be where you try to push your changes to see how it actually works before you push it to the actual live site. For you to know is that you should consider each of these environments basically as completely separate Umbraco sites. They all have their own file system. They have their own database, and actually run their own web application on what could be different web servers. You should actually consider three really, really separate sites that you need to manually synchronize for this to work. Defined workflow in a setup like this is that you do changes on the left side and then push the changes to the right side, which means that whatever you do will always be tested on the first site, and when you confirm that it's working, you're gonna be pushing it to the next one and then on to the next one. The only cases where you would actually have stuff traveling from the right to the left is when you do restores. That also happens when you instantiate your setup, but it's most likely from left to right always. So for this to work, we need to do some synchronization. There's two kinds of synchronization happening on U.S. File system and web server. File system is usually used for structural data, such as document types, data types, all the development things you see in Umbraco, member types, style sheets, all that stuff. All of this is structural data, and it should essentially always be equal in all your environments at some point. This is persistent to the file system in U.S. We basically take the types, serialize it to an object, and whenever you want to transfer it, we take it to the destination, deserialize it, and put it back into the database. So we kind of handle that stuff for you. The other thing is content. We can't really do the same thing for that because content is not always the same on all environments. So we have to synchronize this by web services, which means that you would actually have to do a deployment of this stuff for it to happen. Since this is basically data, or rows in your database, we would need the file system changes with the structural stuff to actually be in order first. So if you want to try to deploy something without actually deploying the structure first, you might see an error, which is basically because we can't fit the content in the structure you have in your sites. So always make sure that your file system is in sync, and then afterwards you can actually deploy content. What you as a developer need to know is that all the file system changes are actually handled by us automatically. Whenever something pops up on the disk, we have a file watcher that actually watches the file system and makes sure that this change will at some point get committed to Git and show up in the portal. Most of you have probably been to the US portal or maybe seen how it works. When we find a file change, it's gonna show up as a commit that should be at some point committed to the next environment. So whatever you put on disk, we will watch and make sure it actually works. So that's taken care of. You don't really need to handle that. What you need to handle is the content and whatever is in the database, because we don't really know what you put in the database. And we don't really know what you want to transfer from one site to another. So as a developer, you need to figure out that these parts have to somehow be taken care of by you. For content parts, this means that you should queue stuff in the UI, for example. In the UI, we do right-click deployments of content, which actually triggers that Courier should serialize this stuff and transfer it by the web services. When this is done, Courier will pick up all the dependencies for this content item and make sure that everything is in place for this content deployment to happen and then transfer it over and extract it. Of course. What you need to know is that basically, the only thing you can actually rely on in this setup is the file system. So if you were to do a package that were doing specific queries in the database, doing maybe a custom table, storing some custom data, we would have no idea about how to handle this. So as a developer, you need to actually make sure that this stuff will work somehow. Somehow let us know how to handle this stuff so it can actually be part of the deployment. We handle all the building stuff in Umbraco, such as duck types, templates, all that stuff. And I mean, we can't really know how to handle your stuff. Another thing you have to keep in mind in regards to packages is the fact that packages are actually, I mean, usually packages can be installed in a site and you just have it work. But the thing is with packages, they can actually end up in a site with nothing else but the files. Since the file system is something we can rely on. So your package needs to be able to handle this scenario. And this is some of the stuff that Sam will get into a little bit later on how to actually kind of survive that dead point where your package just get deployed without anything else. What you need to do, or what you want to do in a BlackRock service is basically you want to stuff, to transfer your stuff from one site to another. And for doing this, we have the tool called Courier or the Courier engine, as we call it. It's basically a tool that watches over your sites and makes sure that everything is synchronized and manages how to transfer data. Courier does this by sending items. Items are small packages that pretty much contains the Umbraco objects, but with some extra added information that we need to actually handle this stuff. Items, in contrast to normal Umbraco items, always have a possibility of adding dependencies, which means that if you have a document, it will have a dependency on a document type. That document type may have dependencies on certain property types. All this kind of builds up a complete dependency tree that we can then, in Courier, pass through and figure out what is actually needed on the destination and what do we already have on the destination and what do we need to deploy in order for this to work. So basically Courier is in charge of building all this stuff up and then making it work. It does sound a little bit simple, but there's a bit more to it. Courier knows how to handle all our data, but we don't really know how to handle yours. Courier can seem a little bit magical once in a while, but even though it does seem like that, it's actually not really that clever. If we consider a property value of, for example, 1055, if we just look at that value, it seems like it could be something we would know about. If Courier looks at it, it's basically just going to be a number. It doesn't really know how to handle that. Courier will just take this value, put it directly on the destination and say, well, I'm happy about it. I'm cool. It all works. I'm really glad. We are not happy, however, because we know that 1055 actually means that this is probably a reference to a concept. A content item or a media item. Which means that in a really good use case, we would actually have this content item or media item transferred along with this other item. How we could do that is actually, we make Courier know some more intelligent stuff about our items. To do this, we use a resolver. A resolver is basically a little piece of code that can hook up with Courier and let Courier know how to handle specific items, such as a media picker. So when Courier loops through all its data, it's going to see, hey, this is a media picker. I have a resolver that actually knows how to handle a media picker. So instead of just transferring the value of 1055, it's going to go down and figure out, well, it's a media item. I'm going to pick up that item, add it as a dependency, and make sure that it actually is transferred along with this. This is one of the things a resolver can do. But as we all know, Raku nodes, in a Raku, are auto-increasing IDs. So it's not always enough just to transfer the item. For content items, for example, we would usually have a reference to that node ID, and then, I mean, if we transfer just the content item, it might get a new ID on the destination. So resolver is also responsible for actually making sure that when this content item is then deployed on the destination, the content or media picker will actually have its value rewritten to what the new ID of that item would be. So the resolver is actually the path that hooks into Courier and tells you how to handle your data. As a developer, you need to make sure that if you ever do a property editor that stores some kind of weird data, we would need some kind of resolver to actually make sure that this can deploy through Raku as a service. So that's what you need to do in regards to this. What you will most likely need as a developer would be the resolver. I've created a simple example here of what a resolver looks like. Basically, this is the inner part of it. You have to actually inherit some interface stuff, but this is the most interesting part. What we do here is we look at the property data of a specific content item. That means we actually look for all the properties on this specific content item and we then check up with the fact that one of them should be matching a rich text editor, which is what we have in the editor alias. If we find one of those properties using a rich text editor, we will pass the value of that property through a simple regex, and that basically looks for any source text referencing something in the media folder, which is where on Raku usually stores its images. By doing this, even though we don't really have an, you know, ID reference to the media item, we can actually find media that is just referenced by media links. When it's done doing that, it will actually try to get the media by that URL using the media service get media by path, which hasn't worked in a couple of years, I think, but I just fixed it for doing this. But this one looks up the media in the database, tries to figure out if it actually is a media item, we have in the database, and if it finds something, well, it's most likely going to be something that we need, so we are going to include it as a dependency. This means that it will actually show up when we try to deploy something using that item. One thing is the dependencies, but how does it actually work underneath all this? Courier is one thing. It has a lot of these providers and resolvers and stuff, and basically Courier is not that clever as I just said. For it to actually take stuff that you would be doing in custom tables, for example, Courier would need an item provider. This could be for your data, it could also be for our data. We have item providers for everything in the call right now, such as document types, templates, all that. So we know that when something is asking for a template with an ID of something, we know how to actually pick up everything that template needs. For a template, that would be the database entries for that template. It would also be the actual template file. The thing responsible for knowing stuff like this about the built-in types is the item providers. So if you were to do a custom table doing something with custom items, which could be products, for example, what you would need to do is a product item provider, which actually knows how to access your database or database layer, and then knows how to actually package this up as, I don't know, a courier item. So, yep, this is some stuff that I think Søren will get a little more in depth about, talking about how he actually used item providers and data resolvers for Ucommerce. So. Oops Mic dropped, yeah. Yay, it worked the first time this time, that so often. So, my name is Søren, and we'll take a look at the practical aspects of this. If you have any questions, put up your paw, and I'll see if I can answer it. So what I'm going to talk about is this, the actual practical aspects of getting this to work. So what are the moving pieces? We've already been introduced to the item provider, the resolver, but what does it look like when you need to implement it? It's not that difficult, so I think once you've seen this, you can actually take the slides I have here and use those as a template to create your own. Before we dive into that, let me just talk a little bit about what Ucommerce is and why we need it. So it's a commerce platform. You probably all know that at this point. The reason we need this stuff is because we store extensive data in our own databases or in the Umbraco database, such as products, categories, catalogs, stuff like that. This information needs to be moved between environments to give you that seamless experience that you saw yesterday. What we also do is we link content and our stuff. Content. Content and catalog. And so those things also need to be picked up and moved between environments, just as we saw. So for us, we needed to do three things. We needed to modify the package a little bit, because as we just learned, the package may find itself in a completely new environment without you actually installing it anywhere, so it needs to be able to deal with that. So we've coined a new term that you'll see in a little bit. We need to deal with structure data. So structure. There are two different types of data. Structure data, like document types, so foundational data, configuration data. If you're doing packages or solutions, you have this probably in some form. And content. So the data that builds on top, so that would be your items in Umbraco. It would be our products. It would be things of that nature, right? So the package installation, what needs to change? Well, this is the old way of doing it, right? You have one environment. You'll install your package. When it's time to go to the... to QA or testing, you'll install the package again. And again, until you're done, right? If you're dealing with a load-balanced environment, you may have 20 servers on there, and you need to go do it over and over again. How many have tried that? Just reinstalling. Only two people. That's awesome. Three, four. Good. So that needs to be changed a little bit, because one of the things that we face is now that we have three environments, and four, actually, the package can get installed anywhere, and then be moved to another environment. So we need to deal with that. And I know Klaus said that you should be moving from left to right, but you know, when users have the ability to install stuff anywhere, you know what will happen? They will totally do it, right? So ideally, stuff should move from left to right, but in actuality, it will probably just sort of happen all over the place. So it needs to be able to deal with that. So one example that we needed to deal with is this, where we install, or you install, on the development environment in Umbraco as a service, and then you pull to a local environment. So basically, just a Git pull, and all of a sudden, the package in Umbraco finds itself in a new situation where there's no database, it's not initialized, so it needs to be able to realize that that's the situation it's in, and then reinitialize itself without actually having you to do anything. Does that make sense? So basically, what it boils down to for you is everything is just much simpler, because you don't have to install anymore. Well, you need to install once, but that's it. Another situation, this is the ideal situation that we'd like to see happen every time you pull down the environment, then you install in the local environment. This is actually very similar for standard packages. This will actually work exactly how packages do today. Package actions will be run, all that stuff will happen, and it will modify the file system and then be pushed to the development environment. Great. But then it needs to reinitialize itself, and that's the thing that not a lot of packages can do. And so just another environment to test on, another scenario to think about if you go down this route. So traditional packages are like this, right? You install them on every environment. You need to deal with it on every environment. You need to update on every environment, make sure that it works. Not a great idea. So today we're coining a new term. We call it the headless package. And the idea is basically this, that you can install on any environment like we just saw, and then you can move it about, and it will actually initialize itself. But to do that, there is a couple of problems. Package actions are not an option because they will not be executed. Basically, the app pool spins up. Once it's spun up, that's it, right? Nothing executes that. I took a look at the thing in Umbraco that's responsible for actually installing packages just to see if I might be able to trigger that manually. Maybe. But I asked a couple of the guys about it, and they said, well, maybe, but probably something will happen. Something untoward, right? So this is the problem. You can't use that. You have to come up with a new way of dealing with this. Your own little installation framework, Umbraco 8, will have package migrations, which will actually do something like that. But today, there is not actually a thing that will do this. So you need to come up with your own thing or execute the package actions, if you will. They are just little pieces of code that you feed with XML. So you could execute them manually. But it might be easier to just code something that can do the things you need, right? Update a database. Something like that. So how do you actually get the package to run? Or your installation? Because there's no user input now to deal with that. This is the way to do it from Umbraco 6.1 and up. Basically, just an application handler that will run when the application starts. That'll work. It runs just by virtue of being in the bin folder, which will happen when the file system gets updated from Git. So that makes sense, right? This is the Umbraco way. Whenever we investigate different ways of doing this, we like to take a look at the Umbraco way and also the standard.net way. Because the Umbraco way has a tendency to change from time to time. You probably noticed this, right? When you upgrade Umbraco, something changed and you just modify it. Not a big deal. But when you're shipping a package that needs to work with a wide range of Umbraco versions, we support the latest all the way back to 6 or something like that. Maybe even 4. So we can't use this stuff. So we need a generic approach as well. So one way of doing it is using .NET. It has this nice little thing called the pre-application startup attribute. So basically it's the thing you put on a class in your assembly. You give it a method to load that corresponds to this one here. And so .NET will actually run this very early in the process before you even start to run any app domain code or anything like this. This starts to run. So this is a great way to run a module, for example. The way we do it is we have an HTTP module that we hook up programmatically. So instead of registering it in WebConfig, we just hook it up here so we don't need to screw around with WebConfig because there are also some issues with that that we'll look at in a little bit. Once that's done, on the first request, we will initialize the system. So we will look at what DLLs we have. We will notice whether DLLs are newer than what was actually installed and we'll then start to kick off the installation process. Does that make sense? So it's fairly simple stuff. This is a recipe you can use this way or the other way. It doesn't really matter. You just need a thing that will run as soon as the app domain starts. The module looks like this. Simple stuff again. You've probably written a ton of these. Remember the double lock pattern. Super important. You don't want the installer to run twice, right? We're running here in a thread, not a thread, a request. And so if multiple requests come in, it will screw you up during installation. You only want one of those to run. So this is also a pattern you can just take and use. Make sense? Good. Oh, here's the installer. You may be able to run it. I don't think so. And it is going away too. So this is a good place to start to think about how to deal with installation. The beauty of this model is that it actually works with NuGet as well. So when you suck down a package from NuGet, if you think about it, that's exactly the same situation you're in. The binaries all of a sudden find themselves in a new website. You hit F5 on that website, it boots up. What do you need to do? In it, right? Exactly the same thing. So if you are thinking about shipping your packages as NuGet, this is actually also a great way to go. So a lot of the stuff that we did for this was actually something that we had done in preparation of releasing our NuGet packages. So we could just take that work and reuse it basically. Web config. How many of you do packages in here? One, two, three, four, five, six, seven, ten-ish? Ten and a half? Three quarters? Okay. How many of you modify web config when you install? One, two, three? One quarter, maybe? Ah, that's a quarter. So that's approximately half. Sometimes you need some stuff in web config, right? There may be some settings, maybe you need to tell .NET something, we need this module, we need to register, whatever, right? No. You can't do it anymore. Not in Umbraco as a service. If you do it like this and content moves from left to right, then you're good. It does actually work. Because you will merge the web config like you've always done, it'll be a new file in the file system and it will be pushed into the production system. That's great. But when you install here, which we know when users have the ability to do it, they will totally do it. It will not work. We found this out the hard way because we installed the package and everything's working great. But then comes along Umbraco as a service and says, hey, you've made a modification to this file, we'll just push it back from the... I'm not sure quite what happens there, but it seems like there's a runtime version of the web config that gets put back into place. And so your changes to web config are gone, and for us it means awesome yellow screens of death, which is never a good user experience, right? So, no. You can't do it. But what you can do is you can use configuration transformations. These are, you know them from Visual Studio, they're named a little differently from what you're used to. But basically the name here in bold corresponds to the actual environment. So you'll have one for each of the environments that you want these to trigger on. So the way we make it work is basically by having one for each environment that will then be merged in as the Umbraco as a service side spins up. And so you'll have a web config that actually has the things you need. But it will not be the web config that is committed to Git, which is important to realize. So when you suck that stuff down to your local environment, it will be a raw web.config. Right? Make sense? That doesn't have these changes. But when you package reinitializing using the previous approach, it will have those changes. So you get into a situation that's a little bit... I'm not quite happy about it, but you get into a situation where web config will look in one way if you behave in a certain way. In a certain manner. And it will look in a different way if you start to pull it down to local. It will probably happen at some point. So at the end of the day, your web config will look like it's always done. But there might be sort of a temporary situation where the package is installed in the live environment or the development environment where it doesn't actually look like that. That's just something to realize. And if you're building packages, remember that you're not the only ones that want to do this. There might be other packages that need that as well. So make sure and be good citizens and just merge your stuff into that like you've done with web config. You should view this as just another way to deal with web config. I've talked to Klaus about maybe we could do something about it, but we'll see. Does that make sense? Good. So with these changes in place, you can now install in any environment. There's no weird stuff happening when you're installing in one environment or the other. And you can even stick NuGet on it as well. Right? So you... You pick. Install it on Umbraco as a service. Install it on your local environment. Grab a NuGet package. Doesn't really matter. You get the same experience. And that's basically what we're after in an installation situation. When you're doing packages, you probably know this, that there is this moment. You have five minutes or something like that. We call it the first five minutes at Ucommerce where you need to convince the user that what they just installed is good. If it doesn't work, yellow screen of death. It doesn't pop up for some reason. It doesn't set itself up. You will be gone and try something different, right? Because you don't have time to screw around with packages. So seamless installation in my world is super important. I've also worked with other products back before the Umbraco days where setting up the thing was a 20-page Word document. It took days to set it up. And once you set it up, you had nothing. No example site, nothing like that. It was just a blank slide and you were like, huh? Right? So you want to try and give the users a good experience on the first five minutes. Make sense? Good. Any questions? One question. Yes. Yes. There are two ways. We are looking at supporting SQL CE for this very scenario to give you that seamless experience. The beauty, what we've done is we can actually run on a separate database. So if you make sure and modify a web config with a separate connection string pointing to a database, it will re-initialize itself into that. So it will work, but it's not as elegant as I'd like it to be. So we're investigating SQL CE. For large commerce installations, SQL CE is really not something that I would recommend. So we haven't supported it so far. But for this scenario, it makes perfect sense. So we're looking into that. Okay. This is the fun part. This is just getting stuff up and running, right? This is what you will be doing all the time, creating data, moving data, getting everything up to speed. So for us, we have a, for the content side of things, we have products, of course, that we need to move about. We have our own database that all that stuff sits in. So we can do multi-language. We can do all sorts of wonderful things with this. So we need to move products about, potentially. Maybe, maybe not. It depends a little bit for commerce. It's pretty common to have your product sitting in an ERP system someplace where you need to integrate from another source. So maybe the environment's just pulled from that. So this wouldn't be necessary. But one thing that you will definitely need to move is stuff like this. This is a demo I did yesterday. So for us, definitions is the way you describe data, like document types, and that you will need to move about. One comment here. If you have data, make sure that you stick some GUIDs on it because we're dealing with different environments now. So migrating data between environments require you to be able to figure out that that data is already there or not. So if you don't have it, make sure you stick it on there. If you're using integers for relationships and things like that, you can still do that and just have a GUID on the side. That's how Bracca does it right now. But ideally, when you start designing this, make sure that you think about GUIDs right off the bat. Got structure data. So like with the package installation, structure data can be created anywhere. It can happen on the development environment, the live environment, and on the cloud. On the live environment and on the local environment. You shouldn't really change anything in the live environment. Some of it should be moved in there so you make sure and test it and things like that. But just something to keep in mind. So what we're going to take a look at is how we get one of these sockets from one environment to the next. What needs to be built. So it's actually fairly simple stuff because if you think about it, when you edit something into the UI, you basically take some data, stick it in the database, right? With Bracca as a service, you stick it in the database and someplace else. That's it. So it goes to the file system as well. So there will be a representation of the entity in the SQL database. You already have this. You're doing this, right? But there will also be a file with all that information in it. And that's the work we need to do to make it movable. This is basically what happens, right? When somebody clicks a button in Bracca, there is something called an event manager. This is what lets Bracca know that something happened. So it basically queues a piece of data to be serialized to the file system. That's the first thing. Second thing is we introduce an item provider. That's one. You'll have an item provider per thing you need to move. So for us, it's one for a definition. It's one for a data type and one for some other things, right? It's per thing. It packs the data and then sends that off for serialization into the file system. That's it. So there is no magic here. Not at all. It's very simple stuff. So what happens? It goes into the file system. So we'll have a few folders in here for the different things. These are the standard folders from Bracca. We sort of discovered that prefixing it would be a good idea. Again, if there are multiple packages, it makes sense that you can see where the files are coming from and you avoid collisions. You probably tried doing right-click menus that required a letter as the identification. Have any of you tried that? So back when you needed to do right-click actions for the tree, the way you identified one of those was giving it a letter. And there are not that many letters in the tree. You're just using the alphabet, right? And you could, if I picked A and somebody else picked A, one of us would win. So you want to avoid those collisions. This is the same thing right here. At the end of the day, you end up with a GUID. This is the GUID of the item and the file in there. And it's just XML. That's it. So this is one of ours. This is, what is it, a definition that's been serialized. That's it. Go ahead. Sorry? Perfect question. I'll answer that in 30 seconds. Before we can start doing it, creating things and reacting to these signals, we need to set up an item provider. So this is basically in the construction of the item provider. You give it some options. And there are two things that are important. The ID of the item provider. This is what will uniquely identify this thing and allow Umbraco to pick it up when an entity needs to be serialized, deserialized. Yeah? And a folder. The other things are things that will show up actually in Umbraco as a service in various places. But these two, the ID and the extraction directory, these are the two important ones. So let's take a look at send to cache. So how does it happen? So in Umbraco, there are a couple of ways that it happens. When you right-click on a node in Umbraco, there's a new menu item way at the bottom that says queue for transfer or something like that. That queues it to be sent off to another server. In other places, it happens transparently. So when you save a data type, it just happens. So you want to find the places in your packages where you need to store this data and hook into that. For Ucommerce, it looks like this. So we have a UI up here that edits it. We have a concept of a pipeline. This is just something, a way we deal with custom business logic where we hook in some information. So you'll have UI events that you need to hook into. You have Umbraco events, something like that. For us, it's just a pipeline. It's a configuration file you roll out. And in there, there are two things that need to happen to notify on completely custom data. First, you need that basic event manager. And it just says send to cache T. So that will be the actual thing that you're sending to cache. And it just queues it to be sent. So it may happen immediately. It may happen later. But it tends to happen almost immediately. The second thing is notice the item identifier here. How many were at Shannon's talk yesterday with Umbraco 8 and roadmap and all that? Two. Two. Ten. Ten. Good. He talked about hive IDs. How many of you remember those? A couple of them. So a hive ID in Umbraco 5 was a way to identify a thing by ID and type, which was actually a pretty good concept. This is almost the same. Because what it does, the identifier, is it identifies the thing with a GUID. So GUID, all the things. Remember that. But it also identifies the type of thing here with another GUID that identifies the item provider that you need to kick off to do things. Makes sense, right? So to actually serialize the stuff, you need to implement a handle pack method. And it is super simple stuff. You've probably done code like this. It basically maps from one type to another, hands it off to Courier, that then saves it to file. So everything is an item as we learned from Klaus. So our definition item here is just something that inherits from that item with the things that we need to be serialized. And we then just do the mapping here. So set a GUID, set a name, set a bunch of other things. I've slimmed it down a little bit for brevity. The other thing that's important are dependencies. So when you have a definition, in our case, it will have fields on it. And those fields will have a data type that determines, is it a lint, is it a rich text, what is it, right? And that, of course, needs to go along with that. So you can see here it's picking up all the data types that are used in the definition, creating a new dependency. And by virtue of doing that, two, three, four, five things are going to be serialized. So that makes sense? You need the things that it relies on. It may be on the other end, but you don't know because you don't know where it's going. So simple stuff. The dependency, basically, it identifies the GUID. It gives it another ID. And this will actually kick off another item provider for a data type. So it has sort of a hierarchy building. Does that make sense? On the other side, it will make sure and deserialize the things that you need. So we'll just put a simple object with some stuff on it to the file system, and we're done. And so we have a file like that in the file system. What it also does is it notifies Mbraku as a service that this happened. That's the basic event manager that does that. And so this button will light up the deploy button that knows that there are a couple of commits. This takes a little time, not a long time, but a little time. And once you do it, it will kick off the push from one environment to another where we need to do the reverse. We need to take those files, turn them into data objects again. So we have a development environment. We do a quick git push, and then we need to handle the deserialize, and then we handle extracts. So two more things, and then we're done. This is where the magic happens right there. This is where it becomes a little complicated because here you need to merge. If you have the same object on two systems, this is where you need to merge the two and figure out what needs to be done. And Mbraku as a service doesn't do that. It will not help with that because it can't. It doesn't know what your particular objects are, so you need to deal with it. And this can be fairly complex stuff, so hence the magic. Good, so let's take a look at it. Handle deserialize, super simple stuff. Basically what it is is you will receive the file here as bytes, so we need a way to transfer those into another version. The serializer will help with that, and it needs a way to know what the bytes should become to turn it into. So basically the important part here is that we specify the thing that it needs to be turned into. But it's simple stuff. This is a model you can just take and use. Next thing is the handle extract. So this is where the magic happens. So again, we're in an item provider, so another piece of this where we get an item. We know that it's an item definitions by way of the item identifier, so we know what item it is and we know which providers, and the provider has been kicked off. And then we start to figure out where, for us, this is our business entity, so you're looking at our data layer here where we're querying that and saying, do we actually have an entity that corresponds to what we just received? And so by using the null coalesce operator, we can set up a new definition, and we can either update the existing one or create an entirely new one and save that to the database. Magic happens here. Again, not that important, but this is where the objects are merged, so the changes are there. So you've got the object kept, last man wins, and then you save it and you're done. Right? Simple stuff. And with that, you'll have the new definition on the other side of the fence, just by clicking that little button. Any questions? Does that make sense? Hopefully. You should be able to take these slides if you need to do this. There's a question down here. I wanted to delve into the magic a little bit. Is there somewhere you can know what environment it's come from? Last man wins is one, so you can put it right beside, if you're going left to right, you can increase it one way, right to left. Yeah, nope. Nope. Just get the thing and, yeah. Any other questions? That I hopefully can answer yes to? Okay. Now, so that's structural data, right? This is the way that you will deal with fully custom data that has nothing to do with Umbraco, but it becomes seamless. And that's the beauty of it. It's just part of the workflow. There's another scenario, probably also very common when you're dealing with custom data. You have a node in Umbraco, and that node has some stuff from another system on it. So in this case here, I've created a little page with promoted products on it. So basically something where you select some things from our custom store, stick it on the page. The problem there, of course, is you're left with a bunch of identifiers for things, right? So when this page needs to be moved by right -clicking on the thing and securing it for tracking, and securing it for transfer, it needs those dependencies from that other store. This is where, to my mind, it becomes beautiful because the two systems are able to interact with each other seamlessly once this is implemented. To do that, we need the item resolvers. And this is a bit of a big mouthful, but basically it's a visitor pattern because what the item resolver needs to do is it needs to realize whether it should actually do anything for that particular thing. So if you're operating with different document types, different fields, different everything, it has to look at all of them to realize whether or not there's something to be done. And you can have multiple of these item resolvers to help you out queuing other stuff for transfer. So the code for that is fairly simple stuff, again, but it's just, as you count them, we're now up to maybe six or seven pieces. But the individual bits and bobs, simple. So first, there's a visitor pattern that is executed that asks the thing, can you help me with anything? As I'm moving this particular node. And it will say yes or no. In our case, we are looking at the data of the provider to see if it's moving one of the things that we're interested in. So in this case, is it a node or a media item or something like that? In this case, it's a node. So, yes. Then we need to figure out whether that particular thing is using a document type with a field on it that we're interested in. So that's what's happening right here. It's asking whether the alias of the picker, this is something we install, is on there. And if it is, it says yes, we can provide some information for that. So simple stuff again, right? Disregard this. This is what happens when you do slides at 2 a.m. in the morning. So let's speed along here. The next thing is once we've said yes, we need to actually go and follow up on that and make it happen. So a little more stuff. But basically what's happening is it's almost like the example that you saw before. We are, again, finding the actual properties that has the picker on it. We're finding the value of that. We are then here splitting up the string with identification in it. Because this is something from before we actually added GUIDs to the platform. It will have ints in it. Damn it. And so we need to convert those things into GUIDs, which happens right here. We are finding the GUID for each product. This actually will get optimized. This is a very slow way to do it. We will do it in one fell swoop. But we are loading up the product. We're finding the GUID. And we're then adding a dependency. And that will kick off another item provider for products. And you can see where this is headed, right? But this is the only thing. So it feeds back into the system we just saw. So we're basically back to item providers that packs, blah, blah, blah. So we can do that one more time for the new things. Does all that make sense? Good. I know it's a little dry stuff because it's data flowing around. But the beauty is that we've done all the dry stuff for this at least. So you don't have to worry about it. You just need to click the buttons you want and it will happen automatically. And that's all there is to it. Are there any questions for this? One question. Okay. So the question is branching and version control in GUID. Is that supported? Yeah. But because at the end of the day, the thing you're getting in is your merged version of that. So you decide what it is. But merge conflicts and sort of screwing up in GUID is going to be a thing. I know because I may have been there a couple of times during this. Any other questions? Yes. So the question is we're talking about infrastructure data. What about the real data? So the products are actually real data in that sense because it's not structural. It is actually like content. Orders, on the other hand, is like would you really be interested in moving orders between systems? You might be. So you synchronize them back so you have something to play with later on. Yeah, yeah. It can work for orders, any data. So it's more a matter of whether it makes sense or not. Maybe moving orders between systems makes sense in certain situations. From our point of view, we grab the low-hanging fruit, which was the catalog, because that you're going to need. Other low-hanging fruit would be campaigns, for example, so for marketing. Yeah. So you're just using different rules for discounts and things like that that you might want to set up on one environment, play around with it, try it out, and then move it to another system. That would be perfect for this as well. Yes? So the data is over, so you're telling the reader how to deploy a new property, a node, and a property that's about it. And item providers are what you're telling is how to deploy something apparently new. Yes. Yeah. So the question is the item provider is that's how we're telling them. We're telling them how to deal with a new concept. And the resolver is about extending something. Yeah, exactly. So the idea is that this media or this material needs to exist all the time. Yes. And to know what the name of the built-in provider is. Yeah. Yeah, you do. So the question is what if we have the reverse situation where we have some custom thing that's linking something in Umbraco? Will I have to know what the ID of that particular item provider is? And yeah, you will. So you have to dig around for that a little bit. I haven't seen any place where you'd see that. Are we there yet? No. We have time for questions. Two more minutes. One more question over here. What if we have a running setup but we've for example on another server? Yeah. And we migrate it to a service. What are you going to do with all the data that's already there, customers and ? Yes. But I'll try. So if you've already set up another environment outside of Umbraco as a service, what do I do to get it into Umbraco as a service? Yeah. So for the stuff that we support, it's actually super simple because you can just pull down the Umbraco as a service stuff. You can click save and you will actually get that file. This is all based on Courier. So you can actually use Courier with this stuff as well. So you can install Courier on the website. And use that to get the file. Once you have the file, it becomes part of the file system. Push it in and it will be recreated. All the other stuff you have to migrate yourself. Okay. So because this is not done yet for customer data. Exactly. And it may never be. So if you have customers and orders and things like that and you want to move that from one environment to another, how to deal with that? The beautiful thing here is that there is access to the databases, the underlying databases in the environments that you have. Okay. So you can migrate in sort of the way that you've done it before. You can access it from your local machine? Yeah. Just to see the production and staging machines? Yes. So that helps quite a bit. Also, sometimes it helps just to figure out what's going on. You can also access via Git the different Git repositories on stage and live and dev. Sometimes that helps in figuring out where things are coming from. Because with this model here, sometimes when, if you're like me and you screw up in Git, you will sometimes see items that just sort of magically appear. But it's typically because you and with you I say me, I did something I shouldn't have done. Another question down here? I'm not sure if you can hear me, but for non-commercial packages, is there any way to get started on your ads without having to pay for it at the time? I would say create a trial account to start out and get in touch if you need more time than you have on that one. Because, I mean, it's a lot of work. We might just give out an account to people who do popular packages to make sure that they actually work. At some point, we will end up supporting packages if they are popular enough. But we like to have as much as possible compatible with Umbraco as a service because it's kind of hard for people to sometimes understand that Umbraco and Umbraco packages is two completely separate things. They can't understand that when someone develops a package, why doesn't it just work on Umbraco in every type of instance? So for us to actually have people do stuff like this by themselves, maybe with some help, is a really great thing. So just get in touch. Another question? Do you think that the desktop units are getting into a documentation on Umbraco? Yeah, some of it is already documented. I'm probably going to take my examples and use that as a base for documentation. Because we have some leggings. There's some basic stuff in Resolvers and a little bit of them in Providers, but I'm pretty sure it's not updated. We have it scheduled for after CodeGarden to actually get that stuff updated. Is that just whenever, after CodeGarden? No, but as in... Sorry about that. I couldn't help myself. The second one is, I know you're a champion of CodeGarden, but I think you're probably going to be a bit more difficult to make this kind of code out of the blade. Yeah. Yeah. In regards to that, you should not be thinking about Umbraco Deploy right now, because it's very far out. So you should really just be focusing on this stuff and make it work. We are going to base our stuff on Deploy on the same concepts, so it's going to be some changes, but it's not going to be a completely different thing you'll be doing. We will still have Resolvers, and we still have Providers, and the same concepts. So, yeah. So, it won't be directly compatible, but it's the same concepts. I think also, if I can add to that, it's important to realize that the heavy lifting is not so much Umbraco as a service as it is just getting data in and out of a database. So they made the extension points super simple to work with. So if they were to change a little bit, the guts of them wouldn't change anyway, because you still have to map from one thing to another. So I think a lot of the stuff would be sort of just... Even if you changed radically, it would still sort of have to do maps from here to there and back. And back, right? Yeah? This is from the board of the Department of Service, which was the most challenging topics on the task. I know that in the future, sometimes there are two things on the stage that I've not done on the development, and that would be to take my... But what I thought, sometimes, that would be the most challenging. Well, we have some defined rules for handling conflicts. I'm not quite certain of how they work. But if we have a conflict we can't resolve using whatever rules we have set up, we will... I think it is deploy from left to right always and just overwrite changes on that part. We do try to resolve conflicts in the nicest way possible. And to be honest, I've been working quite a lot with this, and I haven't really seen any, like, conspicuous behavior of it. So it seems to actually be doing it the right way. We have all this... We have all this really weird logic that just kind of resolves things in whatever way you would probably think it should be resolved. But, I mean, if you're seeing strange behavior, please also get in touch because it might be something we haven't thought about. And there is also... It's just files at the end of the day, so the way the file is created will also sort of make or break how many sort of conflicts you'll have. So you have a list of things. If something changes in there and over there, that's a conflict that can't be resolved. But if that list is a dictionary, on the other hand, it will be serialized as separate lines that actually can be merged. So you can also be smart about the way that you deal with, at least when it's custom data, the way that you structure the file that comes out of the process. Yeah. And the thing is, as we said earlier, it's all separate sites, all separate databases. So you have connection strings to everything. You can just connect directly to it. It's also a separate Git repository, so you can always go in and check what actually happened during one of our automatic merges. We actually had a little accident with some sites last week where we had to kind of roll back some changes, which means we basically just connect to the Git repository, take the commit that did the wrong changes, and did a Git rollback on that, which actually made the whole thing work. So it's not like we try to disable it. We try to destroy data on purpose, but sometimes we do have to roll back. Yeah. Yeah. I mean, those cases are fortunately not happening that often. I haven't seen any of them happening in a real site so far. So if you do have it happening, you can do it. If it's happening, let us know. We will try to fix it. All right. Any other questions? Going once. Okay. Good. Thank you very much. Any other questions. No no some of you. Okay. Very good. Thank you. Thank. Thank you.