<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[MellonDev Blog]]></title><description><![CDATA[Development, cloud and home office]]></description><link>https://mellondev.net/</link><image><url>https://mellondev.net/favicon.png</url><title>MellonDev Blog</title><link>https://mellondev.net/</link></image><generator>Ghost 4.36</generator><lastBuildDate>Mon, 30 Mar 2026 03:49:38 GMT</lastBuildDate><atom:link href="https://mellondev.net/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[#100daysofcode Week 6]]></title><description><![CDATA[Week 6 of my #100daysofcode challenge, delving into Enterprise Angular development with Microfrontends.  Webpack 5 with Angular 11.next pre-release]]></description><link>https://mellondev.net/100daysofcode-week-6/</link><guid isPermaLink="false">5f7c0cd493b7a80bc83b6770</guid><category><![CDATA[Angular]]></category><category><![CDATA[Enterprise Architecture]]></category><category><![CDATA[microfrontend]]></category><category><![CDATA[Webpack]]></category><dc:creator><![CDATA[Craig Mellon]]></dc:creator><pubDate>Fri, 16 Oct 2020 06:35:05 GMT</pubDate><media:content url="https://mellondev.net/content/images/2020/10/blog-banner-week-6.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://mellondev.net/content/images/2020/10/blog-banner-week-6.jpg" alt="#100daysofcode Week 6"><p>Quite a mixed week for my challenge, I&apos;ve played around with GitHub Actions, read the Practical Module Federation book, added messaging in my shell and played around with the new Angular CLI v11.0.0-next.4 build.</p><h3 id="angular-v11-0-0-next-4">Angular v11.0.0-next.4</h3><p>The new version of Angular is on the horizon and included in some of the latest builds is Webpack 5 compatibility. &#xA0;This enables the use of Webpack 5 with the Angular CLI and with that would enable Module Federation to be used to build Microfrontend&apos;s. &#xA0;I downloaded a couple of the v11-next versions and managed to get v11-next.4 to build an application using Webpack 5. &#xA0;I then used Manfred Stayer&apos;s ngx-build-plus to use a custom Webpack config and configured Module Federation. &#xA0;Everything builds fine and the remoteEntry.js file is generated but for some reason the Public Path setting is not working, the shell is loading the remote Microfrontend but then when loading the chunks its trying to resolve them from the shell&apos;s base URL. &#xA0;I also came across another problem where the Shared modules option in Module Federation was throwing an error during the build, I didn&apos;t spend too much time on this due to the Public Path issue, I&apos;m not sure if it is a problem with the latest Webpack RC or the angular build. &#xA0;Will spend some time next week to try and get this working as this will improve the development experience massively and also enable the use of the latest Angular version.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/manfredsteyer/ngx-build-plus"><div class="kg-bookmark-content"><div class="kg-bookmark-title">manfredsteyer/ngx-build-plus</div><div class="kg-bookmark-description">Extend the Angular CLI&#x2019;s default build behavior without ejecting, e. g. for Angular Elements - manfredsteyer/ngx-build-plus</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/favicons/favicon.svg" alt="#100daysofcode Week 6"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">manfredsteyer</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://avatars3.githubusercontent.com/u/1573728?s=400&amp;v=4" alt="#100daysofcode Week 6"></div></a></figure><p>Currently I&apos;m using a custom build of the Angular CLI v9 to enable Webpack 5 use, and although it works I can only build in Production mode and every time I make a change I have to rebuild the app using Webpack. &#xA0;Also the sass styles within a component are not being built correctly, the Angular build must be doing something funky to compile these a scoped styles, I spent quite a bit of time trying to get this working and just couldn&apos;t, I had to resort to have multiple global SASS files for the styles.</p><h3 id="day-36">Day 36</h3><p>Today I setup a separate GitHub workflow to build and deploy my Azure Function Api. &#xA0;This was needed as the existing Azure Static Web App functions can&apos;t be used by other websites due to CORS configuration.</p><p>I setup the workflow to only trigger on changes to the Api folder and now when pushing in changes the Api is built and deployed into my Function App. &#xA0;All works great and the setup was pretty easy due to many provided GitHub Actions.</p><h3 id="day-37">Day 37</h3><p>Finished the Practical Module Federation book today. &#xA0;The book has 2 parts with the first part covering the details of Module Federation and then the second part covers a number of different usage scenarios. &#xA0;The second part was really good, and gives you plenty of ideas on when you can use Module Federation, everything from Microfrontend&apos;s to shared components between backend Admin apps and frontend consumer websites.</p><h3 id="day-38">Day 38</h3><p>Added a new events service to the Shell-Core library and worked on getting the Feature Browser Microfrontend publishing an install event when the user selected to install a new feature. &#xA0;The shell listens for the event and processes it to dynamically load the Microfrontend into the shell. &#xA0;So good to see this working and really gets your thinking of all the areas in Enterprise Applications where it can be used.</p><p>Below are some screenshots of the Feature details page, and then a screenshot of the menu with the new feature installed after clicking the install button. &#xA0;The navigation is dynamically updated when the feature is installed.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://mellondev.net/content/images/2020/10/feature-microfrontend-details.PNG" class="kg-image" alt="#100daysofcode Week 6" loading="lazy"><figcaption>Features Microfrontend showing the feature details page</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://mellondev.net/content/images/2020/10/shell-new-feature-microfrontend-installed.PNG" class="kg-image" alt="#100daysofcode Week 6" loading="lazy"><figcaption>Shell menu showing the dynamically installed feature link to the Microfrontend</figcaption></figure><h3 id="day-39">Day 39</h3><p>Angular CLI 11.0.0-next.4 was released today so I downloaded it and created a new Shell project and configured it to use Webpack 5 for the build. &#xA0;I also added a custom Webpack config with Module Federation details and got the application building. &#xA0;Great news is that the app is building fine with the ng build command and using Webpack 5. &#xA0;Will spend some time on this over the next few days, will build a separate Microfrontend and try and load into the shell.</p><h3 id="day-40">Day 40</h3><p>Had another go at the Angular CLI v11-next builds, I created an additional Microfrontend project within the same Angular Workspace. &#xA0;I setup separate builds and custom Webpack configs for the projects and added some of the dynamic Module Federation code into the shell to load the Microfrontend. &#xA0;All is building fine and both projects are generating the RemoteEntry.js files and the shell is also loading the remote Microfrontend entry file.</p><p>The bad news is the Module Federation build is not using the Public Path configuration so when the shell is trying to load a remote Microfrontend its trying to resolve the chunks using the shell&apos;s base url. &#xA0;Still its getting close and just being able to use the ng build commands will be a massive move forward for the development experience. I feel like its so close, so might wait for a v11-next.5 build and hopefully update my current shell and Microfrontend to use Angular 11 instead of the custom Angular 9 build I&apos;m using</p><h3 id="day-41">Day 41</h3><p>Watching some of Manfred Steyer&apos;s videos on Domain Driven Design from NG Conf. &#xA0;Thinking about how the domain driven aspect helps with how you might create vertical slices with microfrontends, microservices and a datastore.</p><h3 id="day-42">Day 42</h3><p>Attempted to get my Angular 11/Webpack 5 application working again today. &#xA0;I created a microfrontend app last week but couldn&apos;t get it to load the remote modules due to it using the current browser location. &#xA0;This was due to the public path setting within the webpack config not being applied. &#xA0;Not sure if this is due to the RC of webpack so I modified the remoteEntry.js and hardcoded the remote URL in and got it working, although only partly as the shared options are not working so its not sharing the angular runtime :(</p>]]></content:encoded></item><item><title><![CDATA[#100daysofcode Week 5]]></title><description><![CDATA[Week 5 of my #100daysofcode challenge, delving into Enterprise Angular development with Microfrontends.  Created the backend and deployed the microfronted fully serverless]]></description><link>https://mellondev.net/100daysofcode-week-5/</link><guid isPermaLink="false">5f684b0915467a138c9f466b</guid><category><![CDATA[Angular]]></category><category><![CDATA[Azure]]></category><category><![CDATA[CosmosDb]]></category><category><![CDATA[Enterprise Architecture]]></category><category><![CDATA[microfrontend]]></category><category><![CDATA[Webpack]]></category><dc:creator><![CDATA[Craig Mellon]]></dc:creator><pubDate>Mon, 28 Sep 2020 06:35:28 GMT</pubDate><media:content url="https://mellondev.net/content/images/2020/09/blog-banner-week-5.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://mellondev.net/content/images/2020/09/blog-banner-week-5.jpg" alt="#100daysofcode Week 5"><p>Good progress has been made this week, in summary I have created a feature browser microfrontend which is backed by an Azure Function API with a Azure CosmosDB for the data. &#xA0;The microfrontend is hosted using Azure Static Web Apps and CosmosDB is deployed using the new serverless option so everything is running in a serverless manner.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://mellondev.net/content/images/2020/09/features-microfrontend-architecture.PNG" class="kg-image" alt="#100daysofcode Week 5" loading="lazy"><figcaption>Features Microfrontend Architecture</figcaption></figure><h3 id="azure-static-web-apps">Azure Static Web Apps</h3><p>For quite a while people have been using Azure storage to host static websites which provides a very low cost serving option as storage is very cheap. &#xA0;This year Azure announced a new static website offering which enhances the storage hosting options and provides some extra features such as Custom Domains, CI/CD integration with GitHub, global availability, integrated serverless APIs and authentication/authorisation.</p><p>The integrated deployment adds a GitHub action to your repository which builds and deploys your static website. &#xA0;Also if there is a azure function in the same repo it will build, provision and deploy the function without any setup. &#xA0;I initially went down this route and must say it worked seamlessly, I created a function API which retrieved a list of features from a cosmos database. &#xA0;When I pushed the code in, it built and deployed both the frontend and the function. &#xA0;All was looking great, however the built in functionality takes a lot of the configuration away from you and the major blocker for use with microfrontends was the CORS configuration. &#xA0;You have no control over the function config and the default CORS configuration only allows the corresponding static website to communicate with it. &#xA0;This meant I couldn&apos;t use the integrated deployment/provisioning for the microfrontend as when used within the shell which is a separate website the function could not be called due to CORS.</p><p>In the end I created a separate function application and deployed my API to that, configured CORS to allow the shell host to call it and all was good. </p><p>Overall I feel like its been a good week with both the shell and features microfrontends working and deployed to the cloud, below is a sneak peak of the shell with the features microfrontend remotely loaded into it.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://mellondev.net/content/images/2020/09/shell-feature-details.PNG" class="kg-image" alt="#100daysofcode Week 5" loading="lazy"></figure><h3 id="day-29">Day 29</h3><p>Setup the backend for my features microfrontend, using Azure Functions and the new serverless version of CosmosDB for the data storage. &#xA0;The microfrontend is also hosted on Azure Static Web Apps so its all serverless</p><h3 id="day-30">Day 30</h3><p>Spent some more time on the backend today. &#xA0;Azure Static Web Apps enables you to deploy a function while deploying the site and it works seamlessly and really easy to setup, however unfortunately it wont work with my microfrontends as it defaults the CORS configuration to only allow calls from the Static Web App. &#xA0;The microfrontends get loaded into a shell and executed within the shell, so any calls to apis made from the microfrontend happen within the shell&apos;s context and url so the api gets blocked by CORS.</p><p>In the end I had to deploy a separate function app and now its all working good.</p><h3 id="day-31">Day 31</h3><p>Added another function to enable the creation of new features in the CosmosDB. Added a create feature component to the features app and hooked it all up. &#xA0;Also had a play around with the Monaco editor but couldn&apos;t get the compilation working with Angular.</p><h3 id="day-32">Day 32</h3><p>Today added functionality to the Features App and microfrontend, I can now add features with Markdown descriptions and view the details of a feature from within the feature browser microfrontend.</p><p>In other news Webpack 5 is now in RC and the angular CLI team recently committed some changes for supporting Webpack 5. &#xA0;Hopefully this means Angular CLI 11 will support Webpack 5 which will mean a more integrated development experience.</p><h3 id="day-33">Day 33</h3><p>Worked on some of the styling today to get the Markdown descriptions rendering within the feature details page. &#xA0;The page is looking good and tested loading the feature browser within the shell and all looks good.</p><p>Angular CLI also releases v11-next.3 today, some more Webpack 5 commits so it doesn&apos;t look like its far off at all. &#xA0;I did download it and had a little play around, updated it to Webpack 5 but unfortunately was getting errors with some of the Webpack plugins. &#xA0;I don&apos;t think full support is there yet so didn&apos;t too much time looking into the build issues</p><h3 id="day-34">Day 34</h3><p>Not much code today, instead I spent some time reading a new book I got from Zack Jackson and Jack Herrington: Practical Webpack Module Federation. &#xA0;The books gives you the details of Module Federation works under the covers. &#xA0;So far very good read.</p><h3 id="day-35">Day 35</h3><p>More reading again today, I&apos;ve almost finished the Practical Module Federation book. &#xA0;Really liking the second part of the book which goes through some usage scenarios.</p><p>One scenario went into the details of how to build reliable web applications which use Module Federation. &#xA0;It goes into the details of how versioning works when resolving dependencies, and then presents an idea of providing the microfrontend as both a remote dependency and also as a local NPM dependency. &#xA0;This enables the web application to fall back to the local NPM version if the remote is not available for any reason. &#xA0;This is an awesome feature, you can develop locally against your microfrontend and even get type safety using shared services/components, but then at runtime you can load the dependency from a remote. &#xA0;This enables you to push out patch releases to the microfrontend and all apps consuming it are then using the new version without having to re-compile and re-deploy. &#xA0;Obviously the versioning needs to be strict so that you don&apos;t push out any breaking changes and Module Federation allows you to set semantic version demands on the remote dependencies, so if you reference ^1.1.6 and push out a new ^1.1.7 version it will load fine, however if you push out v2.0.0 it wont load the remote and then you can fall-back to your local version.</p><p>I plan on doing some blog posts around this, however I want to wait until the new Angular CLI supports Webpack 5 as the setup a build is a little fragile at the moment. &#xA0;I want to create some blog posts which guide the developer through the process of setting up and creating an Enterprise Microfrontend application.</p>]]></content:encoded></item><item><title><![CDATA[#100daysofcode Week 4]]></title><description><![CDATA[Week 4 of my #100daysofcode challenge, delving into Enterprise Angular development with Microfrontends.  Created new microfrontend app and setup dev/prod configurations]]></description><link>https://mellondev.net/100daysofcode-week-4/</link><guid isPermaLink="false">5f683b6a15467a138c9f456d</guid><category><![CDATA[Angular]]></category><category><![CDATA[Dev Containers]]></category><category><![CDATA[Enterprise Architecture]]></category><category><![CDATA[Webpack]]></category><category><![CDATA[microfrontend]]></category><dc:creator><![CDATA[Craig Mellon]]></dc:creator><pubDate>Mon, 21 Sep 2020 06:51:43 GMT</pubDate><media:content url="https://mellondev.net/content/images/2020/09/blog-banner-week4.png" medium="image"/><content:encoded><![CDATA[<img src="https://mellondev.net/content/images/2020/09/blog-banner-week4.png" alt="#100daysofcode Week 4"><p>Its been a busy week again this week, mainly focused on getting a second microfrontend project created which is then dynamically loaded into the shell.</p><p>The project is created and there is a separate GitHub repo for it so it can be built and deployed independent of the shell. &#xA0;Below is a sneak peak of how the shell looks with the features micorfrontend loaded. &#xA0;The features navigation item at the top is dynamically added from a microfronted array and when clicked the route is lazy loaded from a remote URL using Webpack&apos;s Module Federation.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://mellondev.net/content/images/2020/09/microfrontend-shell.JPG" class="kg-image" alt="#100daysofcode Week 4" loading="lazy"><figcaption>Screenshot of the Microfrontend Shell and the feature microfrontend</figcaption></figure><h3 id="day-22">Day 22</h3><p>Created my second microfrontend today, a feature browser to view a list of features available to load into the shell. &#xA0;Each feature is a microfrontend and selecting to add a feature will add it to the Shell configuration and load it remotely into the application.</p><p>Had some Webpack issues again which is no surprise, everything was compiling ok but when loading it into the shell it was throwing chunk loading errors. </p><h3 id="day-23">Day 23</h3><p>Good day today, fixed the Webpack config issue I had after sleeping on it :) I now have a separate microfrontend project which is dynamically and remotely loaded into the shell. &#xA0;Plan is to work more on some shared services provided by the shell to enable the microfrontends to integrate with the shell.</p><h3 id="day-24">Day 24</h3><p>Another great day today, got some shared libraries in order using the Module Federation configuration and now my second microfrontend is sharing the already loaded Angular Material libraries instead of redownloading. &#xA0;Original size of the microfrontend been loaded was 485kb, after marking the Angular Material libraries as shared the loading size went down to 35kb.</p><p>I did hit another issues today with my dev container and the network. &#xA0;I have been using a local NPM server to serve up a package for the Shell-Core library but because I&apos;m using a dev container it cannot access other containers on the network. &#xA0;After a bit of digging I found the issue and updated my dev containers to connect to the bridge network which then allowed them to access other containers running on the local machine.</p><h3 id="day-25">Day 25</h3><p>Playing around with styles again today. Yesterday I got the angular material libraries shared between the shell and microfrontend, however my style overrides for the material card were not been applied. I noticed looking through dev tools that there were some material styles loaded after my own style sheets. Did some digging and found out the material components dynamically add the base styles when loading. Spent some time looking at how to customise this and found a solution which involved configuring Webpack to include my styles in the body rather than the head, this ensured that my styles would override the injected material styles</p><h3 id="day-26">Day 26</h3><p>Setup some GitHub actions today to build and deploy my microfrontends to azure static web apps. So now whenever I push into master it will automatically be deployed to azure static web apps, and best of all its free ATM as it&#x2019;s in preview.</p><p>Checkout my GitHub repo below to see the microfrontend code along with the deployment actions.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/mellondev/microfrontend-features"><div class="kg-bookmark-content"><div class="kg-bookmark-title">mellondev/microfrontend-features</div><div class="kg-bookmark-description">Contribute to mellondev/microfrontend-features development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/favicons/favicon.svg" alt="#100daysofcode Week 4"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">mellondev</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://avatars3.githubusercontent.com/u/55980527?s=400&amp;v=4" alt="#100daysofcode Week 4"></div></a></figure><h3 id="day-27">Day 27</h3><p>Separated the dev and prod Webpack configs today along with a common base config and then used merge for the specifics. Got the apps deploying using prod mode and running local in dev mode.</p><p>All code is available on my GitHub page, there are 2 repos for the microfrontend work I&#x2019;m doing, the shell and a features microfrontend</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/mellondev"><div class="kg-bookmark-content"><div class="kg-bookmark-title">mellondev - Overview</div><div class="kg-bookmark-description">mellondev has 4 repositories available. Follow their code on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/favicons/favicon.svg" alt="#100daysofcode Week 4"><span class="kg-bookmark-author">GitHub</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://avatars3.githubusercontent.com/u/55980527?s=400&amp;u=b77606cdcb8e3d8aee98f7f49527d517948368eb&amp;v=4" alt="#100daysofcode Week 4"></div></a></figure><h3 id="day-28">Day 28</h3><p>More Webpack config today, I created separate dev/prod configs yesterday but the AotCompiler options were not replacing the environment.ts file. In the end had to create separate main.ts and bootstrap.ts to manually set it to production mode</p>]]></content:encoded></item><item><title><![CDATA[#100daysofcode Week 3]]></title><description><![CDATA[Week 3 of my #100daysofcode challenge, delving into Enterprise Angular development Microfrontends.  Added new microfrontend repo, fixed some sass webpack issues and improved the look of the shell]]></description><link>https://mellondev.net/100daysofcode-week-3/</link><guid isPermaLink="false">5f610d3d9457dd19b07a236e</guid><category><![CDATA[Angular]]></category><category><![CDATA[Enterprise Architecture]]></category><category><![CDATA[VS Code]]></category><category><![CDATA[microfrontend]]></category><category><![CDATA[Webpack]]></category><dc:creator><![CDATA[Craig Mellon]]></dc:creator><pubDate>Tue, 15 Sep 2020 19:45:31 GMT</pubDate><media:content url="https://mellondev.net/content/images/2020/09/blog-banner-week3.png" medium="image"/><content:encoded><![CDATA[<img src="https://mellondev.net/content/images/2020/09/blog-banner-week3.png" alt="#100daysofcode Week 3"><p>Last week I created a list of tasks I would like to do as the first week was a bit all over the place, I managed to complete most of the tasks and was planning on doing this same this week but never got around to creating a list.</p><p>This week has mostly been spent on adding some style to the shell, I don&apos;t know why but when I&apos;m prototyping I feel like I progress more when it looks better. &#xA0;</p><p>One of the big issues I have yet to figure out with the custom webpack builds is getting the scoped component sass files to compile. &#xA0;For some reason when they are referenced in the component it wont compile them, throwing a document type error. &#xA0;It works fine when using an app wide sass file, but not with the scoped component level files. &#xA0;I spent a good few hours on this and in the end decided to give up, and took the approach of using global styles and will break them up and reference them into the main sass file.</p><p>Towards the end of this week I created a separate repo for a microfrontend, I wanted to see how working on the microfrontend in isolation would work out. &#xA0;I got a new project setup, building and loading into the shell, so overall its been a pretty decent week.</p><h3 id="day-15">Day 15</h3><p>I played around with the new pre-release of Angular 11 today, hoping that the new webpack 5 type support would enable me to use the build in custom web pack builders with webpack 5. &#xA0;Unfortunately I was hitting some new errors so quickly decided to leave it and wait until more webpack 5 support is added. &#xA0;I have it working in Angular 9 and will continue down this path to experiment with microfrontends.</p><p>I also began adding authentication to my shell application, I created a free Auth0 account and then tried the angular-oauth2-oidc library created by Manfred Stayer. &#xA0;This library looks promising, but for some reason just wasn&apos;t working correctly with Auth0, it was constantly refreshing the screen. &#xA0;I was version 9 of the library and after reading through the docs more I saw that Auth0 was not supported until v10 :(.</p><h3 id="day-16">Day 16</h3><p>I continued trying to add authentication to the shell and decided to use Auth0&apos;s SPA library which worked great. &#xA0;Just added the NPM package, injected the service and setup the config and it all worked first time.</p><h3 id="day-17">Day 17</h3><p>Mainly been playing around with the styles again, I dont know why I just can&apos;t leave them alone, I&apos;ve spent so many hours messing about I really need to let it go :).</p><p>Spent a bit of time in Photoshop today, been a while since I last touched Photoshop. &#xA0;I wanted to play around with the look and feel of the shell app and with the custom builds and sass issues it was too much effort to play around in css. &#xA0;The design I did included the MellonDev logo faded into the background of the shell, looked good in Photoshop so then tried to apply it using css.</p><p>I added the image to the sass file using background-image css and then when compiling it wasn&apos;t working. &#xA0;The compiled sass was trying to reference the image as an object. &#xA0;I didn&apos;t spend too much time on it, I decided to do some more reading about webpack builds and style-sheets.</p><h3 id="day-18">Day 18</h3><p>Not so much coding today, my MSDN Azure account ran out of credits so the MellonDev blog was down. &#xA0;I spent some time looking at alternatives, I&apos;ve always liked the idea of static site generators but never had time to try them out, so today was a great opportunity to give it a try.</p><p>I spent a few hours reading up on some tools and decided to give Hugo a go. &#xA0;They have some great docs and also quite a few different themes that can be used. &#xA0;My thoughts were to create a new blog using Hugo and then publish it using Azure Static Web Apps which are free at the moment. &#xA0;Azure Static Web Apps is built on top of Azure Storage so I think they will be pretty cheap when out of preview.</p><p>I downloaded Hugo, created a few pages and applied a theme, all seems to work great and with a bit more effort I could get the blog building when pushing changed into github. &#xA0;I&apos;m just playing around locally at the moment but can see the benefits of using this going forward. &#xA0;</p><p>Rather than lose focus on the microfrontend project I&apos;m working on I decided to leave Hugo for another day, I don&apos;t want to spend my free time sorting my blog out when I can be working on this project.</p><h3 id="day-19">Day 19</h3><p>Webpack config again today, I decided I&apos;d tackle the background-image issue. &#xA0;Although the background-image is not a show stopper I believe these little issues help with learning, I know more about webpack configs than ever before, Angular does a good job of hiding all the details for you so I&apos;ve never had a need to learn it before.</p><p>Reading through the docs and reading quite a few stack-overflow articles I finally fixed the issue. &#xA0;I needed to configure webpack with a file-loader plugin which can be configured to handle the file references.</p><h3 id="day-20">Day 20</h3><p>Coding today rather than config. &#xA0;I started creating a new shared library that encapsulated the authentication services. &#xA0;The idea with this is to enable the microfrontends to use this service to get access to the authenticated user details.</p><h3 id="day-21">Day 21</h3><p>I&apos;ve made it to the end of week 3. &#xA0;The microfrontend project is coming along nicely.</p><p>Created a new repo with a separate microfrontend project for managing the features (microfrontends) within the shell. &#xA0;This microfrontend will provide a UI to the shell to enable the user to browse through all the available microfrontends and click to add one into the shell dynamically.</p><p>The shell will load and show the microfrontend to browse the features and then when the user selects to add a feature the microfrontend will interact with the shell&apos;s shared service to add the feature and load it into the shell</p>]]></content:encoded></item><item><title><![CDATA[#100daysofcode Week 2]]></title><description><![CDATA[Week 2 of my #100daysofcode challenge, delving into Enterprise Angular development Microfrontends.  Added authentication, a little style and a shared library for the shell]]></description><link>https://mellondev.net/100daysofcode-week-2/</link><guid isPermaLink="false">5f4a3a32d8b1e35d9594e433</guid><category><![CDATA[Angular]]></category><category><![CDATA[Enterprise Architecture]]></category><category><![CDATA[microfrontend]]></category><category><![CDATA[VS Code]]></category><dc:creator><![CDATA[Craig Mellon]]></dc:creator><pubDate>Mon, 07 Sep 2020 06:23:30 GMT</pubDate><media:content url="https://mellondev.net/content/images/2020/09/blog-banner-week2.png" medium="image"/><content:encoded><![CDATA[<img src="https://mellondev.net/content/images/2020/09/blog-banner-week2.png" alt="#100daysofcode Week 2"><p>So the first week was a little all over the place, I watched and read a lot of material on microfrontends, <a href="https://webpack.js.org/">Webpack</a>, <a href="https://nx.dev/angular">nrwl nx</a> and devcontainers. &#xA0;My main goal for this #100daysofcode is to investigate developing microfrontends with angular and to see how the workflow would work in an enterprise application.</p><p>So far I have managed to get a shell application loading remote microfrontends and sharing a service between the microfrontends to enable communication and access to shell functionality such as authentication, user profile and notifications.</p><p>To try and stay more focused this week below is a list of what I was aiming to complete:</p><ol><li>Create a new Shell Application which looks less like a prototype</li><li>Add authentication to the shell</li><li>Create a shared shell library and provide access to the authenticated user details such as tokens for calling backend services</li><li> Create a microfrontend which displays the logged in user, calls a dummy backend with the users auth token and adds a notification to the shell</li></ol><h3 id="day-8">Day 8</h3><p>Trying to build a shared library within the shell/microfrontend workspace. &#xA0;Created a new library project and built but for some reason could not reference it within the shell project. &#xA0;Also the build output seems different to the usual Angular library output, which I suspect is down to the custom Webpack config and build. &#xA0;I was trying to build the library with the Shell so I could use tsconfig paths and not have to keep rebuilding the library when I make changes.</p><p>Spent a few hours on this and gave up, not sure I fully understand what the custom Webpack build is doing, so need to understand that more.</p><p>So I created a new library project within a new workspace and published an npm package to a local npm server (Verdaccio). &#xA0;Installed this into my microfrontend shell and it still couldn&apos;t see the module :(. Decided that was enough for today, come back to it tomorrow with a clear head.</p><blockquote>Verdaccio is great for local development, runs within a docker container, one line and it&apos;s up and running. &#xA0;Great to test publishing of an npm package</blockquote><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://verdaccio.org/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Verdaccio &#xB7; A lightweight open source private npm proxy registry</div><div class="kg-bookmark-description">A lightweight open source private npm proxy registry</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://verdaccio.org/img/favicon/favicon.ico" alt="#100daysofcode Week 2"><span class="kg-bookmark-author">Gatsby</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://verdaccio.org/img/logo/symbol/svg/verdaccio-tiny.svg" alt="#100daysofcode Week 2"></div></a></figure><h3 id="day-9">Day 9</h3><p>So I finally got to the bottom of why my library wasn&apos;t been found in my shell project. &#xA0;I was building the library and then npm publishing it from the source folder instead of the dist folder &#xA0;&#x1F926;&#x200D;&#x2642;&#xFE0F;</p><p>After fixing why my shell wasn&apos;t seeing the module I got on with trying to use and share a service from the library, all looking good in the ide but when building it blew up :(. &#xA0;Because I created the library in a new workspace it created it using Angular 10, and importing an Angular 10 library into an Angular 9 app doesn&apos;t work. &#xA0;So downgraded the library to Angular 9 and all working now.</p><h3 id="day-10">Day 10</h3><p>I have my library installed and working in my shell, so today I tried adding it to one of the microfrontends. &#xA0;The service I created is a simple notification service with a createNotification method and a list of notifications. &#xA0;The idea is for the shell and microfrontend to share the same instance and both be able to add notifications to it.</p><p>My first attempt failed :( for some reason the microfrontend was getting its own instance. &#xA0;Did some reading and updated the library to expose a module and use the forRoot static method to provide the service, but again still didn&apos;t work :(.</p><p>I finally got this working after a couple of hours, I forgot to update my shell and microfrontends Webpack config to mark the library as shared. &#xA0;Once I did that it worked!.</p><p>A great day today, finally have the barebones of an Angular shell loading external microfrontends which are using some core shell services to communicate and interact with the shell.</p><h3 id="day-11">Day 11</h3><p>After a really good day yesterday, today has not been too good. &#xA0;I&apos;ve spent hours trying to get the sass styles to compile with the custom webpack builds and cannot get it working.</p><p>I guess these are the sort of issues I&apos;m going to come across when using unreleased frameworks.</p><h3 id="day-12">Day 12</h3><p>Still stuck on my webpack 5 config with custom angular builds. &#xA0;My apps and html templates are building fine, but when adding the style sheets in it just fails :(. &#xA0;I&apos;ve tried so many different combinations of sass loaders and config that I&apos;m starting to forget what I&apos;ve already tried.</p><p>I did feel like I was getting closer though, found an old repo with a custom webpack config for an angular 8 app and noticed it was using a different loader for the .ts files, it was using an AngularTemplate loader. &#xA0;Gave that a try and the app and html still build fine, but the sass still doesn&apos;t</p><h3 id="day-13">Day 13</h3><p>Had another attempt with the sass/css compilation, got a little further but still no luck.</p><p>Angular released their v11-next version of the cli and looking through the commit logs webpack 5 support is not far off, there is a couple of commits adjusting types for webpack 5. &#xA0;Hopefully they will update to use webpack 5 soon and I may revisit it then.</p><p>Added a bit of style to the app with a toolbar/content/footer layout using flex. &#xA0;For now I have added a root sass style sheet now compiling this as part of the app.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://mellondev.net/content/images/2020/09/microfrontend-shell-1.PNG" class="kg-image" alt="#100daysofcode Week 2" loading="lazy"><figcaption>Initial microfrontend shell</figcaption></figure><h3 id="day-14">Day 14</h3><p>Not so much coding today, thought I would have a break from the webpack config and do some reading. &#xA0;I&apos;ve read a great article on feature flags within angular and been thinking about how feature flags along with microfrontends could work together. &#xA0;Could be quite interesting within an enterprise application</p>]]></content:encoded></item><item><title><![CDATA[#100daysofcode Week 1]]></title><description><![CDATA[My first week into the #100daysofcode challenge, delving into Enterprise Angular development with Nrwl Nx, Micro Frontends and also Dev Containers for team development]]></description><link>https://mellondev.net/100daysofcode-week-1/</link><guid isPermaLink="false">5f410cb385ae63f72138fc51</guid><category><![CDATA[VS Code]]></category><category><![CDATA[Dev Containers]]></category><category><![CDATA[Visual Studio Codespaces]]></category><category><![CDATA[Nrwl Nx]]></category><category><![CDATA[Angular]]></category><category><![CDATA[Enterprise Architecture]]></category><dc:creator><![CDATA[Craig Mellon]]></dc:creator><pubDate>Fri, 28 Aug 2020 07:40:41 GMT</pubDate><media:content url="https://mellondev.net/content/images/2020/08/blog-banner-week1.png" medium="image"/><content:encoded><![CDATA[<img src="https://mellondev.net/content/images/2020/08/blog-banner-week1.png" alt="#100daysofcode Week 1"><p>Rather than do a post everyday of my progress I thought it best to do a weekly post so I can keep a log of what I&apos;ve been doing.</p><h3 id="so-how-did-the-first-week-go">So how did the first week go</h3><p>So far its gone pretty well, I usually get up quite early so I have a couple of hours every morning to do my #100daysofcode challenge before work. &#xA0;The weekends are a bit more challenging with two toddlers and the wife working, although I do usually get a couple of hours when my youngest goes for her nap.</p><p>This first week has mostly been research and playing around with stuff. &#xA0;I have spent a lot of time reading and watching talks on Microfrontends and also Nrwl Nx. &#xA0;I have a mono repo setup using Nx workspaces and have been exploring the additional tools Nx gives you.</p><p>Nx uses cypress and jest for tests which is new to me, I&apos;m really liking cypress for the e2e tests. &#xA0;Nx also has a great dependency graph tool which shows graphically what your dependencies are, this is great as most dependency graphs are created manually and quickly go out of date, this tool uses your projects and gives you a live dependency graph.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://mellondev.net/content/images/2020/08/nx-dependency-graph.png" class="kg-image" alt="#100daysofcode Week 1" loading="lazy"><figcaption>Nx dependency graph</figcaption></figure><h2 id="day-1">Day 1</h2><p>Created an Angular Workspace with some library projects to see how they work together. &#xA0;I like the workspace idea for managing multiple projects, I&apos;ve been a long time c# .net developer so I&apos;m used to this style with Visual Studio Solutions.</p><p>Started reading the <a href="http://nx.dev">Nrwl Nx</a> documentation and watched the intro videos.</p><h2 id="day-2">Day 2</h2><p>Setup a new mono repo with an Nx Workspace. &#xA0;Just followed the Nx tutorial and got an app, library and node api set up. &#xA0;</p><p>Explored the e2e tests with Nx which uses cypress, so just playing around with the project for now.</p><h2 id="day-3">Day 3</h2><h3 id="vs-code-dev-containers">VS Code Dev Containers</h3><p>Exploring VS Code dev containers. &#xA0;Setup a dev container config file and added a custom docker image to do the development in based on a cypress/node image.</p><p>Really loving the idea of dev containers, from within VS Code you can select to open a folder or GitHub repo in a dev container. &#xA0;If the repo has a dev container settings file it will use that to build a local docker image based on the settings. &#xA0;This means you have a docker container all setup with the tools required for the repo you are about to clone/open. &#xA0;Also the dev container can specify which VS Code extensions to use for the project, so you can ensure you have all the tooling required without having to spend hours setting everything up.</p><p>This allows me to have all the tools isolated in a container and not polluting my computer, working in a large development team I can see this bringing massive benefits.</p><p>My dev container is tailored for Angular, Nx and Cypress. &#xA0;The container pre-installs the Angular cli , Nx cli and also pre-installs cypress all ready for browser testing.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://mellondev.net/content/images/2020/08/vscode-dev-containers.png" class="kg-image" alt="#100daysofcode Week 1" loading="lazy"><figcaption>Visual Studio Code Dev Containers</figcaption></figure><h3 id="visual-studio-codespaces">Visual Studio Codespaces</h3><p>As well as dev containers I&apos;ve been looking at Code Spaces, these are virtual environments which run in Azure Cloud, I can have an environment all setup and running remotely, which I can then connect to using VS Code, a browser or Visual Studio.</p><p>This is similar to dev containers but all running remotely. &#xA0;The benefits I can see from Codespaces is the performance, it would allow you to code on a more powerful machine, so you could say have a low powered laptop with VS Code installed and remotely develop on your more powerful remote environment. &#xA0;Also the VS Code in browser editor is pretty neat, I can connect to my environment from the browser, this could potentially allow development from say a chrome book and could be great to quickly jump onto a bug or review a pull request even if you haven&apos;t got your dev machine to hand.</p><h2 id="day-4">Day 4</h2><p>Today I finally had a play around with my main goal of the #100daysofcode challenge, microfrontends.</p><p>I pulled a couple of example repos, explored the code and then added a new microfrontend app to a shell. &#xA0;This was based on a standard Angular shell with webpack 5 module federation. &#xA0;It was pretty straightforward to add a new one, although I need to spend some more time understanding how it really works.</p><p>After creating a new app I begun exploring how they could communicate to the shell, I wanted to have a notification area in the shell and then for each microfrontend to post notifications to the shell. &#xA0;I played around with a shared service but couldn&apos;t quite get it working, so the next few days will be working on that and trying out a couple of different ideas such as a message-bus, events or the shared service approach.</p><p>My initial thoughts are to have a shared core shell library that each microfrontend references which include some shared types and some shared services. &#xA0;These services will enable the different microfrontends to have some interaction with the shell. &#xA0;Examples I thought about where the notifications, user/auth service etc..</p><p>Overall a good day, but left feeling I have a lot more questions and research to do.</p><h2 id="day-5">Day 5</h2><p>Started to spend some time trying to understand webpack a lot more. &#xA0;I decided to complete a Udemy course on it and have almost completed it. &#xA0;The course covers webpack 5 and the configuration, so provides a good intro to how to set it up. &#xA0;There is also a new module on the course covering Module Federation.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.udemy.com/course/webpack-5-complete-developers-guide-2020/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Webpack 5: Getting Started 2020</div><div class="kg-bookmark-description">&lt;p&gt;Webpack 5 Course For Beginner/Intermediate Developers&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;This course is &lt;strong&gt;UP-TODATE&lt;/strong&gt; with the latest Webpack version 5 (@NEXT version)&lt;/p&gt;&lt;p&gt;Webpack 5 still not officially released&lt;/p&gt;&lt;p&gt;It is available as webpack@next on npm&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;At the end of the course &#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.udemy.com/staticx/udemy/images/v6/favicon-196x196.png" alt="#100daysofcode Week 1"><span class="kg-bookmark-author">Udemy</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://img-a.udemycdn.com/course/480x270/3122310_ad31.jpg" alt="#100daysofcode Week 1"></div></a></figure><h2 id="day-6">Day 6</h2><p>Finished the Udemy course on webpack and understand how the plugins work and how to use multiple configs together. &#xA0;Also did a bit of coding today playing around with building a library that will be shared between the microfrontends to provide some basic interactions with the shell. </p><h2 id="day-7">Day 7</h2><p>Spent a coupe of hours again today trying to get the Angular library to build along with the microfrontends, just can&apos;t seem to get it work. &#xA0;The microfrontends build fine, and the library does build but not in the usual Angular Library format, which I suspect is due to the custom builder. </p><p>What I&apos;m hoping to get is a shell angular app which also has a shell library application with some shared services. &#xA0;The idea then is each microfrontend will reference the shell library (via an npm package) which will then give it access to some shell functionality, like authentication, user, notifications.</p><h3 id="version-hell">Version hell</h3><p>The one issue with using a shared library is that the microfrontends could all end up with different versions of the said library. &#xA0;If we are using a monorepo for all the microfrontends then this will be fine as they will all share the same dependencies, but if they are developed in separate repos then there is potential for version mismatch. </p><p>I read through a great article from <a href="https://twitter.com/ManfredSteyer">Manfred Steyer</a> which goes into the details of how shared libraries and versioning works when using webpack Module Federation. There is a great example showing how webpack handles the version mismatch and it all comes down to Semantic Versioning, so as long as the versioning strategy is correct then we should be good.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.angulararchitects.io/en/aktuelles/getting-out-of-version-mismatch-hell-with-micro-frontends-based-upon-webpack-module-federation/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Microfrontends: Escaping Version Hell with Module Federation - ANGULARarchitects</div><div class="kg-bookmark-description">Semantic Versioning, Fallbacks, and Singletons</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.angulararchitects.io/wp-content/uploads/2019/03/favicon-180x180.png" alt="#100daysofcode Week 1"><span class="kg-bookmark-author">ANGULARarchitects</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.angulararchitects.io/wp-content/uploads/2020/08/top-img-text-2.png" alt="#100daysofcode Week 1"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[#100 Days of code]]></title><description><![CDATA[<!--kg-card-begin: markdown--><h1 id="why">Why</h1>
<!--kg-card-end: markdown--><p>I read and watch a lot development content and recently have been watching Manfred Steyer&apos;s angular enterprise architecture talks. &#xA0;Years ago while at the Angular Connect conference in London there was a demo of the new lazy loading features available. &#xA0;After the demos there was</p>]]></description><link>https://mellondev.net/100-days-of-code/</link><guid isPermaLink="false">5f3c3ab485ae63f72138fb4e</guid><category><![CDATA[Angular]]></category><category><![CDATA[Enterprise Architecture]]></category><category><![CDATA[microfrontend]]></category><category><![CDATA[microservices]]></category><dc:creator><![CDATA[Craig Mellon]]></dc:creator><pubDate>Tue, 18 Aug 2020 21:13:45 GMT</pubDate><media:content url="https://mellondev.net/content/images/2020/08/blog-banner-2.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h1 id="why">Why</h1>
<!--kg-card-end: markdown--><img src="https://mellondev.net/content/images/2020/08/blog-banner-2.png" alt="#100 Days of code"><p>I read and watch a lot development content and recently have been watching Manfred Steyer&apos;s angular enterprise architecture talks. &#xA0;Years ago while at the Angular Connect conference in London there was a demo of the new lazy loading features available. &#xA0;After the demos there was a Q&amp;A and I asked &quot;would it be possible to lazy load from a different URL with the idea of providing a microfrontend along with a microservice&quot;. &#xA0;The response I got was it may be possible but why would you want to do that. &#xA0;</p><p>The new lazy loading features and the thought of microfrontends got my brain flowing with ideas. &#xA0;In my day job we develop enterprise applications and have quite a few angular applications. &#xA0;Most of these applications have very similar functionality which has led to the development of some shared micro-services which multiple application use. &#xA0;This is all good for sharing the backend services but what I noticed was a lot of the applications had developed very similar UI&apos;s which used the microservices.</p><p>So I always have plenty of ideas flowing around in my head but never seem to get the time to explore these ideas. &#xA0;Watching Manfred Steyer&apos;s talks about enterprise angular architecture has sparked my interest in exploring this idea again, so to make sure I actually follow through with this I have set myself the #100daysofcode challenge to ensure I make time to explore these ideas.</p><p>I&apos;ve put together a list of things I want to explore during my #100daysofcode and also put a final goal statement for something to aim for.</p><!--kg-card-begin: markdown--><h1 id="whatidliketodo">What i&apos;d like to do</h1>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><ul>
<li>
<p>Investigate structure of enterprise applications with Angular</p>
<ul>
<li>Mono-repos</li>
<li>Nx Workspaces</li>
</ul>
</li>
<li>
<p>Create an application shell</p>
</li>
<li>
<p>Create a micro-frontend that can be loaded into the shell as libraries</p>
<ul>
<li>Potentially enabling a micro-frontend to be installed using an NPM package</li>
</ul>
</li>
<li>
<p>Themeing, Microfrontend to take on the application shell theme</p>
</li>
<li>
<p>Integration with the shell</p>
<ul>
<li>Navigation Menu integration</li>
<li>Notifications</li>
<li>Authentication</li>
<li>Security Roles/Permissions
<ul>
<li>Microfrontend should be able to provide permissions to application which can be managed in the user management areas of the shell</li>
</ul>
</li>
</ul>
</li>
<li>
<p>Investigate ability to dynamically add features which would dynamically load microfrontends into the shell</p>
<ul>
<li>VSCode market place, admins can browse and select features</li>
<li>Investigate a feature been a microservice with a microfrontend</li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h1 id="finalgoal">Final goal</h1>
<!--kg-card-end: markdown--><p>The final goal I would like to achieve at the end of my #100daysofcode is</p><!--kg-card-begin: markdown--><blockquote>
<p>Angular application shell for hosting multiple microfrontends which can be used to build up an enterprise application.  The microfrontends can be developed within a mono-repo which includes the shell or can be developed in isolation to the main application, this would enable development of new features which can be easily added to a new project via npm or even dynamically from a market place. Each feature would provide the frontend and will integrate with the shells core features such as navigation and authentication. The ultimate goal would be an application where microfrontends can be added from a marketplace similar to how VSCode extensions are managed</p>
</blockquote>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Entity Framework Core and CosmosDb Provider]]></title><description><![CDATA[A first look at the new Entity Framework Core CosmosDb provider, this post will examine how the CosmosDb provider maps the entities]]></description><link>https://mellondev.net/entity-framework-and-cosmosdb/</link><guid isPermaLink="false">5e95c75840346218b4bccf88</guid><category><![CDATA[CosmosDb]]></category><category><![CDATA[Azure]]></category><category><![CDATA[Entity Framework Core]]></category><category><![CDATA[EF Core]]></category><dc:creator><![CDATA[Craig Mellon]]></dc:creator><pubDate>Tue, 14 Apr 2020 14:43:51 GMT</pubDate><media:content url="https://mellondev.net/content/images/2020/04/efcore-love-cosmosdb.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://mellondev.net/content/images/2020/04/efcore-love-cosmosdb.jpg" alt="Entity Framework Core and CosmosDb Provider"><p>I&apos;ve been wanting to play around with the Cosmos DB provider for a while, so when EF Core 3 was released I decided its time to take a look.</p><p>I use Azure Cosmos DB for a few projects and have always found the SDK to feel clunky, although the new version 3 SDK looks much better. &#xA0;I also use Entity Framework Core in many of my projects and really like the simplicity of using it, so i&apos;m excited &amp; hopeful about the new provider.</p><p>The Cosmos DB provider will help remove a lot of that boiler plate code we have to implement when using the SDK. &#xA0;The provider will enable us to use the Entity Framework and all the good things it provides along with Cosmos DB for persistence.</p><blockquote>For this post I&apos;m assuming you have some knowledge of Cosmos DB and Entity Framework. &#xA0;Microsoft docs are great for an introduction to Cosmos DB and Entity Framework if not<br><a href="https://docs.microsoft.com/en-us/ef/core/">Entity Framework Core Docs</a><br><a href="https://docs.microsoft.com/en-us/azure/cosmos-db/introduction">Cosmos DB Docs</a></blockquote><p>So lets get started and have a play around with it...</p><h1 id="cosmos-db">Cosmos DB</h1><p>I&apos;m going to use a Cosmos instance that I&apos;ve already created in Azure, if you don&apos;t have an Azure account you can create one for free to give Cosmos a try or alternatively you can install and use the <a href="https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator">Cosmos DB Emulator</a></p><blockquote>NOTE: The emulator currently only works on Windows</blockquote><h1 id="project-setup">Project setup</h1><p>You can view this posts sample at the following github repo<br><a href="https://github.com/CraigMellon/efcore-cosmosdb">CraigMellon/efcore-cosmosdb no-readme</a></p><p>You will need to download and install the .NET Core 3.0 SDK which can found here: <a href="https://dotnet.microsoft.com/download/dotnet-core/3.0">https://dotnet.microsoft.com/download/dotnet-core/3.0</a></p><p>Lets create a new empty console app using the dotnet CLI and add Entity Framework Core and the Cosmos provider nuget packages.</p><pre><code>dotnet new console
</code></pre><p>Install <a href="https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Cosmos/">Microsoft.EntityFrameworkCore.Cosmos Nuget Package</a></p><pre><code>dotnet add package Microsoft.EntityFrameworkCore.Cosmos
</code></pre><blockquote>Adding the Cosmos nuget package will also add Entity Framework Core package as a dependency</blockquote><h3 id="lets-get-started">Lets get started</h3><p>For the first experiment we will use a simple <code>Job</code> model with an embedded <code>Address</code> type:</p><pre><code class="language-csharp">    public class Job
    {
        public Guid Id { get; set; }
        public Address Address { get; set; }
    }

    public class Address
    {
        public string Line1 { get; set; }
        public string Line2 { get; set; }
        public string Line3 { get; set; }
        public string Town { get; set; }
        public string PostCode { get; set; }
    }
</code></pre><p>We need to create a DbContext and configure it to use the Cosmos provider, add a new <code>JobContext</code> file with the following code</p><pre><code class="language-csharp">    public class JobContext : DbContext
    {
        public DbSet&lt;Job&gt; Jobs { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
            optionsBuilder.UseCosmos(
               &quot;*YOUR-COSMOSDB-ENPOINT*&quot;,
                &quot;*YOUR-KEY*&quot;,
                &quot;EFCoreTest&quot;
            );
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity&lt;Job&gt;().OwnsOne(j =&gt; j.Address);
        }
    }
</code></pre><blockquote>For simplicity we have hard-coded the endpoint and key into the DbContext, I wouldn&apos;t recommend doing this in a real application</blockquote><p>We are overriding the <code>OnModelCreating</code> method to configure the <code>Address</code> as an owned entity of <code>Job</code>, this will ensure that the <code>Address</code> entity is embedded into the <code>Job</code> document when saved</p><p>Now that we have the DbContext and models setup lets update the <code>Program.cs</code> to create a <code>Job</code> and save it into our Cosmos database using EF Core</p><pre><code class="language-csharp">        Console.WriteLine(&quot;Welcome to the EFCore Cosmos DB Provider...&quot;);

        var job = new Job
        {
            Id = Guid.NewGuid(),
            Address = new Address
            {
                Line1 = &quot;21 Some Street&quot;,
                Line2 = &quot;Somewhere&quot;,
                Town = &quot;Birmingham&quot;,
                PostCode = &quot;B90 4SS&quot;,
            }
        };

        using (var context = new JobContext())
        {
            context.Database.EnsureCreated();

            context.Add(job);

            context.SaveChanges();
        }

        using (var context = new JobContext())
        {
            var loadedJob = context.Jobs.First();
            Console.WriteLine($&quot;Job created and retreived with address: {job.Address.Line1}, {job.Address.PostCode}&quot;);
            }
</code></pre><blockquote>The <code>context.Database.EnsureCreated()</code> will create a collection in your Cosmos DB if it doesn&apos;t already exist</blockquote><p>If we run this code it will add a document into our Cosmos DB, and if we take a look in the Azure Portal we can see that a new collection named JobContext has been created and a new document has been added.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://mellondev.net/content/images/2020/04/efcore-cosmos-collection-initial.PNG" class="kg-image" alt="Entity Framework Core and CosmosDb Provider" loading="lazy"><figcaption>efcore cosmos JobContext default collection name</figcaption></figure><p>By default the cosmos provider will use the name of your context as the collection name. &#xA0;This can be overridden in the <code>OnModelCreating</code> method of your DbContext.</p><p>Lets set the default name of the collection to <strong>Jobs</strong>, re-run our app and take a look at our database again in the portal. &#xA0;Add the following line to the top of the <code>OnModelCreating</code> method in our <code>JobContext.cs</code></p><pre><code class="language-csharp">modelBuilder.HasDefaultContainer(&quot;Jobs&quot;);
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://mellondev.net/content/images/2020/04/efcore-cosmos-collection-renamed.PNG" class="kg-image" alt="Entity Framework Core and CosmosDb Provider" loading="lazy"><figcaption>efcore cosmos job collection name set with HadDefaultContainer</figcaption></figure><h3 id="embedded-collections">Embedded collections</h3><p>As well as embedding entities within your document you can also embed collections which will embed the entities as an array in your document. &#xA0;To test this out we will add a new <code>Contact</code> entity and add a collection of contacts to our <code>Job</code>.</p><pre><code class="language-csharp">    public class Job
    {
        public Guid Id { get; set; }
        public Address Address { get; set; }

        public List&lt;Contact&gt; Contacts { get; set; }
    }

    public class Contact
    {
        public string Title { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string TelephoneNumber { get; set; }
    }
</code></pre><p>Also update the <code>JobContext</code> <code>OnModelCreating</code> method to configure the Contacts on <code>Job</code> as an owned collection using the <code>OwnsMany</code> method</p><pre><code class="language-csharp">        modelBuilder.Entity&lt;Job&gt;().OwnsMany(j =&gt; j.Contacts);
</code></pre><p>Now update the <code>Program.js</code> to add a list of contacts to our job.</p><blockquote>We now have multiple documents in our collection so the call <code>context.Jobs.First()</code> will no longer guarantee to retrieve our new job, instead we will update it to fetch the Job with the Id we just created</blockquote><pre><code class="language-csharp">Console.WriteLine(&quot;Welcome to the EFCore Cosmos DB Provider...&quot;);

var job = new Job
{
    Id = Guid.NewGuid(),
    Address = new Address
    {
        Line1 = &quot;21 Some Street&quot;,
        Line2 = &quot;Somewhere&quot;,
        Town = &quot;Birmingham&quot;,
        PostCode = &quot;B90 4SS&quot;,
    },
    Contacts = new List&lt;Contact&gt;()
    {
        new Contact { Title = &quot;Mr&quot;, FirstName = &quot;Craig&quot;, LastName = &quot;Mellon&quot;, TelephoneNumber = &quot;34441234&quot; },
        new Contact { Title = &quot;Mrs&quot;, FirstName = &quot;Cara&quot;, LastName = &quot;Mellon&quot;, TelephoneNumber = &quot;53665554&quot; }
    }
};

using (var context = new JobContext())
{
    context.Database.EnsureCreated();

    context.Add(job);

    context.SaveChanges();
}

using (var context = new JobContext())
{
    var loadedJob = context.Jobs.First(x =&gt; x.Id == job.Id);
    Console.WriteLine($&quot;Job created and retreived with address: {job.Address.Line1}, {job.Address.PostCode}&quot;);
    Console.WriteLine($&quot;  Contacts ({job.Contacts.Count()})&quot;);
    job.Contacts.ForEach(x =&gt;
    {
        Console.WriteLine($&quot;    Name: {x.FirstName} {x.LastName}&quot;);
    });
}
</code></pre><p>If we now have a look on the portal we can see the Contacts have been embedded into our Job as an array</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://mellondev.net/content/images/2020/04/efcore-cosmos-job-with-contacts.PNG" class="kg-image" alt="Entity Framework Core and CosmosDb Provider" loading="lazy"><figcaption>efcore cosmos contact embedded in job document</figcaption></figure><h3 id="multiple-models-in-one-collection">Multiple models in one collection</h3><p>The Cosmos provider supports storing multiple entity types in the same collection. &#xA0;To do this EF Core adds a field named <code>Discriminator</code> to our documents. If we take a look at the last document in the database we can see the objects each have a <strong>Discriminator</strong> field.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://mellondev.net/content/images/2020/04/efcore-cosmos-job-discriminator-highlight.PNG" class="kg-image" alt="Entity Framework Core and CosmosDb Provider" loading="lazy"><figcaption>efcore cosmos model discriminator highlighted</figcaption></figure><p>Also notice the <code>id</code> column which is unique for each item in the Cosmos collection/partition, EF Core generates the value by concatenating the discriminator and the primary key values, using &apos;|&apos; as a delimiter</p><h4 id="linked-entities-documents">Linked Entities/Documents</h4><p>As mentioned above EF Core can store multiple entities within the same collection. To see how this works lets create a new entity called Resource and add it as a linked entity to our <code>Job</code>.</p><p>First create a new model for our <code>Resource</code> and add a new property to our <code>Job</code> named <code>AssignedResource</code> with a type of <code>Resource</code>, we will also add <code>AssignedResourceId</code> which will give us access to the Id of the resource linked to the <code>Job</code>. &#xA0;If we don&apos;d add this field EF Core will automatically add it under the covers but we wont have easy access to it without this property.</p><pre><code class="language-csharp">public class Job
{
    public Guid Id { get; set; }
    public Address Address { get; set; }

    public List&lt;Contact&gt; Contacts { get; set; }

    public Guid AssignedResourceId { get; set; }
    public Resource AssignedResource { get; set; }
}

public class Resource
{
    public Guid Id { get; set; }
    public string Title { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string TelephoneNumber { get; set; }
}
</code></pre><p>Now we need to update our <code>JobContext</code> to configure the <code>Resource</code> as a linked entity using the <code>HasOne</code> method on the model builder.</p><pre><code class="language-csharp">protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    ...

    modelBuilder.Entity&lt;Job&gt;().HasOne(j =&gt; j.AssignedResource);
}
</code></pre><p>Update the <code>Program.cs</code> to now create a resource with the job</p><pre><code class="language-csharp">Console.WriteLine(&quot;Welcome to the EFCore Cosmos DB Provider...&quot;);

var job = new Job
{
    Id = Guid.NewGuid(),
    Address = new Address
    {
        Line1 = &quot;21 Some Street&quot;,
        Line2 = &quot;Somewhere&quot;,
        Town = &quot;Birmingham&quot;,
        PostCode = &quot;B90 4SS&quot;,
    },
    Contacts = new List&lt;Contact&gt;()
    {
        new Contact { Title = &quot;Mr&quot;, FirstName = &quot;Craig&quot;, LastName = &quot;Mellon&quot;, TelephoneNumber = &quot;34441234&quot; },
        new Contact { Title = &quot;Mrs&quot;, FirstName = &quot;Cara&quot;, LastName = &quot;Mellon&quot;, TelephoneNumber = &quot;53665554&quot; }
    },
    AssignedResource = new Resource
    {
        Id = Guid.NewGuid(),
        Title = &quot;Mr&quot;,
        FirstName = &quot;Bob&quot;,
        LastName = &quot;Builder&quot;,
        TelephoneNumber = &quot;0800 1234567&quot;
    }
};

using (var context = new JobContext())
{
    context.Database.EnsureCreated();

    context.Add(job);

    context.SaveChanges();
}
</code></pre><p>If we run this code and take a look in the portal we can see that two documents have been created. &#xA0;One for our <code>Job</code> and one for our <code>Resource</code>, also notice that EF Core has set the <code>AssignedResourceId</code> field in our <code>Job</code> to the id of the new <code>Resource</code> document:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://mellondev.net/content/images/2020/04/efcore-cosmos-jobe-with-resource.PNG" class="kg-image" alt="Entity Framework Core and CosmosDb Provider" loading="lazy"><figcaption>efcore cosmos job with separate resource document</figcaption></figure><h4 id="loading-linked-entities">Loading linked entities</h4><p>Normally to fetch our <code>Resource</code> along with the <code>Job</code> we would use the Include() method when fetching the <code>Job</code>, in the relational world EF Core would translate this to a join on the backend and return the <code>Job</code> with the <code>Resource</code> populated. &#xA0;If we try to do this with the Cosmos provider EF Core throws an exception informing us that joins are not yet supported.</p><blockquote>The current version of the Cosmos provider does not support joining entities so we can&apos;t retrieve a <code>Job</code> with the <code>Resource</code> in one go. This feature is on the road map and hopefully it will drop into the 3.1 release</blockquote><p>So how can we get the <code>Resource</code> populated on the <code>Job</code> when fetching from the database?</p><p>We could load the <code>Job</code> first and then load the <code>Resource</code> after as we have the <code>AssignedResourceId</code> on the <code>Job</code>. &#xA0;Lets try that out and see how it would work.</p><p>First we need to add a DbSet for the <code>Resource</code> to our DbContext:</p><pre><code class="language-csharp">    public DbSet&lt;Resource&gt; Resources { get; set; }
</code></pre><p>Next lets update the loading portion of our code in <code>Program.cs</code> to fetch the resource and assign it to the job, then finally lets output the resource name to the console:</p><pre><code class="language-csharp">using (var context = new JobContext())
{
    var loadedJob = context.Jobs.First(x =&gt; x.Id == job.Id);

    // now load the resource and assign it to the Job
    var resource = context.Resources.First(x =&gt; x.Id == loadedJob.AssignedResourceId);
    loadedJob.AssignedResource = resource;

    Console.WriteLine($&quot;Job created and retreived with address: {job.Address.Line1}, {job.Address.PostCode}&quot;);
    Console.WriteLine($&quot;  Contacts ({job.Contacts.Count()})&quot;);
    job.Contacts.ForEach(x =&gt;
    {
        Console.WriteLine($&quot;    Name: {x.FirstName} {x.LastName}&quot;);
    });

    Console.WriteLine($&quot;  Assigned Resource: {loadedJob.AssignedResource?.FirstName} {loadedJob.AssignedResource?.LastName}&quot;);
}
</code></pre><p>If your code ran successfully you should see output similar to below:</p><pre><code>Job created and retreived with address: 21 Some Street, B90 4SS
  Contacts (2)
    Name: Craig Mellon
    Name: Cara Mellon
  Assigned Resource: Bob Builder
</code></pre><p>Although this works its not the cleanest code, hopefully we will be able to include linked resources in the next version of the Cosmos provider.</p><h4 id="loading-multiple-linked-entities">Loading multiple linked entities</h4><p>Another scenario I thought about is when we have multiple jobs all linked to the same <code>Resource</code>. &#xA0;How could we load them efficiently?</p><p>When we fetch an entity with EF Core it begins tracking that entity in the DbContext. &#xA0;If we know the <code>Resource</code> before hand maybe we could pre-load the resource into the DbContext and then when loading a <code>Job</code> which is linked to the <code>Resource</code> hopefully EF Core will automatically reference the <code>AssignedResource</code> to the entity already in the DbContext.</p><p>To test this option I will perform the following steps:</p><ul><li>Create a <code>Resource</code> entity</li><li>Create 2 <code>Job</code> entities</li><li>Assign the <code>Resource</code> to the 2 jobs</li><li>Save to the database</li><li>Create a new DbContext instance (this will ensure we have a clean DbContext with no tracked entities)</li><li>Load the <code>Resource</code></li><li>Load the 2 <code>Job</code> entities</li><li>Check to see if the AssignedResource has automatically been set by EF Core using the pre-fetched <code>Resource</code></li></ul><p>Great news is this seems to work, so if the linked entity is already been tracked by EF Core then when you fetch your entities which link to it EF Core will automatically reconstruct your entities</p><pre><code class="language-csharp">var resourceId = Guid.NewGuid();

var resource = new Resource
{
    Id = resourceId,
    Title = &quot;Mr&quot;,
    FirstName = &quot;Bob&quot;,
    LastName = &quot;Builder&quot;,
    TelephoneNumber = &quot;0800 1234567&quot;
};

var job1 = new Job
{
    Id = Guid.NewGuid(),
    Address = new Address
    {
        Line1 = &quot;Job 1 Address&quot;
    },
    AssignedResource = resource
};

var job2 = new Job
{
    Id = Guid.NewGuid(),
    Address = new Address
    {
        Line1 = &quot;Job 2 Address&quot;
    },
    AssignedResource = resource
};

using (var context = new JobContext())
{
    context.Database.EnsureCreated();

    context.Add(job1);
    context.Add(job2);

    context.SaveChanges();
}

using (var context = new JobContext())
{
    var loadedResource = context.Resources.First(x =&gt; x.Id == resourceId);

    // Load all jobs with the same assigned resource id
    var jobs = context.Jobs.Where(x =&gt; x.AssignedResourceId == resourceId).ToList();

    jobs.ForEach(job =&gt;
    {
        Console.WriteLine($&quot;Job: {job.Id} - Resource: {job.AssignedResource?.FirstName} {job.AssignedResource?.FirstName}&quot;);
    });
}
</code></pre><h1 id="summary">Summary</h1><p>Overall I&apos;m liking the the new Cosmos provider for EF Core, It simplifies the creation and querying of your documents with Cosmos significantly.</p><p>Granted the scenarios above are simple, I still have many more advanced scenarios I&apos;d like to try. (<em>Maybe another post</em>):</p><p>The current limitations of the Cosmos provider are documented here:<br><a href="https://docs.microsoft.com/en-us/ef/core/providers/cosmos/limitations">Cosmos Provider limitations</a><br><a href="https://github.com/aspnet/EntityFrameworkCore/issues?page=1&amp;q=is%3Aissue+is%3Aopen+Cosmos+in%3Atitle+label%3Atype-enhancement+sort%3Areactions-%2B1-desc">Cosmos provider feature backlog</a></p>]]></content:encoded></item><item><title><![CDATA[Home Office]]></title><description><![CDATA[Overview of my journey to creating the ultimate home office. Wall mounted monitors and PC, no visible cables and some LED lighting]]></description><link>https://mellondev.net/homeoffice/</link><guid isPermaLink="false">5e949f24b556d60de40d1ba6</guid><category><![CDATA[Home Office]]></category><category><![CDATA[Office Setup]]></category><category><![CDATA[Watercooled PC]]></category><dc:creator><![CDATA[Craig Mellon]]></dc:creator><pubDate>Mon, 13 Apr 2020 17:19:37 GMT</pubDate><media:content url="https://mellondev.net/content/images/2020/04/Blog-Header.jpg" medium="image"/><content:encoded><![CDATA[<h2 id="whats-this-about">Whats this about</h2><img src="https://mellondev.net/content/images/2020/04/Blog-Header.jpg" alt="Home Office"><p>When I moved into my new house there was an office shed out back that was previously used as a home gym. &#xA0;It was a great size, insulated and would make the perfect home office, so I set about creating my own space away from the house to do my coding.</p><p>This post will be picture heavy and contain numerous photos of the office as it is. &#xA0;I still have a few tweaks I want to make so expect a future post when the new additions arrive.</p><p>The office was quite a distance from where my WiFi router was so to start with I had to run a cat6 cable all around the house which ended up been just under 50m in length. Below is some pictures of how the office started and my network cable run.</p><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_2526-1.JPEG" width="1536" height="2048" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_2526-1.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_2526-1.JPEG 1000w, https://mellondev.net/content/images/2020/04/IMG_2526-1.JPEG 1536w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0910.JPEG" width="1536" height="2048" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0910.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0910.JPEG 1000w, https://mellondev.net/content/images/2020/04/IMG_0910.JPEG 1536w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0909.JPEG" width="1536" height="2048" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0909.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0909.JPEG 1000w, https://mellondev.net/content/images/2020/04/IMG_0909.JPEG 1536w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0908.JPEG" width="1536" height="2048" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0908.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0908.JPEG 1000w, https://mellondev.net/content/images/2020/04/IMG_0908.JPEG 1536w" sizes="(min-width: 720px) 720px"></div></div></div></figure><h2 id="the-desk">The Desk</h2><p>I spent many hours looking for the right desk, I really liked the all white setups but at the same time thought they looked a bit cold and not very relaxing. &#xA0;I saw a great picture on Pinterest which featured a dark brown desktop with dark grey walls, as soon as I saw it I had decided that was the look I want. &#xA0;The dark wood with grey drawers and walls looked calming and relaxing.</p><p>To build the desk I used 2 x Ikea Karlby worktops and 3 grey Alex Drawers and under counter beer fridge for my soft drinks and snacks. &#xA0;The reason for 2 worktops was the back wall was over 3 meters long and I wanted it to rung all along the back wall and the largest worktop available was 2.4 meters.</p><ul><li><a href="https://www.ikea.com/gb/en/p/karlby-worktop-walnut-veneer-00335201/">Ikea Karlby Worktop</a></li><li><a href="https://www.ikea.com/gb/en/p/alex-drawer-unit-black-brown-40342285/">Grey Alex Drawer</a> (<em>Grey colour no longer available</em>)</li><li><a href="https://amzn.to/2y8gK31">Inventor Beer Fridge</a></li></ul><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0968-2.jpg" width="2000" height="1500" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0968-2.jpg 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0968-2.jpg 1000w, https://mellondev.net/content/images/size/w1600/2020/04/IMG_0968-2.jpg 1600w, https://mellondev.net/content/images/2020/04/IMG_0968-2.jpg 2016w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0971-1.jpg" width="2000" height="1500" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0971-1.jpg 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0971-1.jpg 1000w, https://mellondev.net/content/images/size/w1600/2020/04/IMG_0971-1.jpg 1600w, https://mellondev.net/content/images/2020/04/IMG_0971-1.jpg 2016w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0938.JPEG" width="2000" height="1500" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0938.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0938.JPEG 1000w, https://mellondev.net/content/images/size/w1600/2020/04/IMG_0938.JPEG 1600w, https://mellondev.net/content/images/2020/04/IMG_0938.JPEG 2048w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/Desk-and-drawers.jpg" width="1512" height="2016" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/Desk-and-drawers.jpg 600w, https://mellondev.net/content/images/size/w1000/2020/04/Desk-and-drawers.jpg 1000w, https://mellondev.net/content/images/2020/04/Desk-and-drawers.jpg 1512w" sizes="(min-width: 720px) 720px"></div></div></div></figure><hr><h2 id="lighting-accessories">Lighting &amp; Accessories</h2><p>To enhance the experience while coding I wanted some accent lighting around the desk. &#xA0;I fitted some wooden planks around the wall and fitted the worktop with a 2 cm gap away from the wall to allow for an led strip to be fitted sunken behind the worktop. &#xA0;This gave a nice glow effect around the desk in both the day and night.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://mellondev.net/content/images/2020/04/IMG_0929.JPEG" class="kg-image" alt="Home Office" loading="lazy"><figcaption>Glow effect around the desk from LED strips</figcaption></figure><blockquote>All LED lighting is Wifi enabled so can be adjusted using my phone and Alexa. Plan to code up some alexa skills to manage the lighting at some point</blockquote><h2 id="no-wires-">No wires!</h2><p>One of my main goals was to create a clean setup, I didn&apos;t want to see any wires anywhere. &#xA0;To achieve this I cutout some socket sized squares in the walls and ran the monitor and PC cables through the wall down under the desk. &#xA0;I got some nice chrome brush covers for the cutouts which look great.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0919-1.JPEG" width="1536" height="2048" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0919-1.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0919-1.JPEG 1000w, https://mellondev.net/content/images/2020/04/IMG_0919-1.JPEG 1536w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0920-1.JPEG" width="1536" height="2048" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0920-1.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0920-1.JPEG 1000w, https://mellondev.net/content/images/2020/04/IMG_0920-1.JPEG 1536w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Before and after hiding wires in cavity wall</figcaption></figure><p>As well as hiding the monitor and PC cables I also wanted all the accessory and extension cables hidden. The plug socket was too low so I couldn&apos;t hide the plugs or wires for the extensions. &#xA0;To improve the look of this I installed a black glass double socket and purchased two 6 socket extensions with nylon braided cables and screwed them to the underneath of the desk. &#xA0;So all you can see when looking under the desk is two cables running up the wall, very minimal and saved having to get an electrician in to move the socket.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://mellondev.net/content/images/2020/04/IMG_0960.jpeg" class="kg-image" alt="Home Office" loading="lazy"><figcaption>Black glass socket and braided cable extension cables</figcaption></figure><h3 id="parts-list">Parts List</h3><ul><li><a href="https://amzn.to/35OZgFH">Brush steel wallplate cover</a></li><li><a href="https://amzn.to/2L3JWLA">Govee 5 meter LED strip</a></li><li><a href="https://amzn.to/2YHW3Gm">Goove TV LED strips</a> (<em>monitors</em>)</li><li><a href="https://amzn.to/2xDOUvo">5m HDMI cable</a></li><li><a href="https://amzn.to/3fv9UWf">Nylon braided extension plug</a></li><li><a href="https://amzn.to/2WbVZwK">Livolo black glass socket</a></li></ul><h2 id="office-essentials">Office Essentials</h2><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0992.jpg" width="2000" height="1500" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0992.jpg 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0992.jpg 1000w, https://mellondev.net/content/images/size/w1600/2020/04/IMG_0992.jpg 1600w, https://mellondev.net/content/images/2020/04/IMG_0992.jpg 2016w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0999-2.jpg" width="1512" height="2016" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0999-2.jpg 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0999-2.jpg 1000w, https://mellondev.net/content/images/2020/04/IMG_0999-2.jpg 1512w" sizes="(min-width: 720px) 720px"></div></div></div></figure><p>Other essentials for the office included a decent chair and a whiteboard. &#xA0;The chair is a SecretLabs Titan and although quite expensive at around &#xA3;350 its well worth it.</p><p>The whiteboard is a 1m x 1.5m magnetic glass board in grey which goes with the room and looks really good, especially with the chalk based pens.</p><ul><li><a href="https://secretlabchairs.co.uk/collections/titan-series#titan_2020-black">SecretLabs Titan Black</a></li><li><a href="https://www.glassboard-shop.co.uk/colourboard_glass_magnetic/grey.html">Grey Magnetic Glassboard</a></li></ul><p>As well as my work space I also wanted a place to step away from the computer and watch some videos or read a book. I had a spare sofa and a spare 42&quot; TV so created a separate space on the other side of the office. &#xA0;The TV is wall mounted and cables run through the walls again, ignore the cables on the floor I haven&apos;t got a TV unit yet :) &#xA0;I also got some large Displate metal posters featuring Doc and Marty from one of my favourite films Back To The Future.</p><ul><li><a href="https://displate.com/displate/47409">Marty metal poster</a></li><li><a href="https://displate.com/displate/920847">Doc metal poster</a></li></ul><figure class="kg-card kg-image-card"><img src="https://mellondev.net/content/images/2020/04/IMG_1004.jpg" class="kg-image" alt="Home Office" loading="lazy"></figure><hr><h2 id="monitors">Monitors</h2><p>At work I have 3 monitors and they are great, but for my home office I wanted something different so went for dual 34&quot; ultra-wide curved. &#xA0;They are huge monitors and great for coding, vs code and visual studio look awesome full screen with plenty of space for all those panels. I can have the explorer, code and the terminal open and still have plenty of free space.</p><figure class="kg-card kg-image-card kg-width-full kg-card-hascaption"><img src="https://mellondev.net/content/images/2020/04/vscode-widescreen.PNG" class="kg-image" alt="Home Office" loading="lazy"><figcaption>VSCode on one monitor with Explorer, Code and Terminal all together with plenty of free space</figcaption></figure><p>The monitors are both wall mounted on adjustable monitor arms and with all cables hidden in the wall cavity. &#xA0;The PC is quite far from the monitors so I had to get some 5m HDMI cables so I could run the cables through the walls and under the desk.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_3080.JPEG" width="2000" height="1500" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_3080.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_3080.JPEG 1000w, https://mellondev.net/content/images/size/w1600/2020/04/IMG_3080.JPEG 1600w, https://mellondev.net/content/images/2020/04/IMG_3080.JPEG 2048w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_3083.JPEG" width="1536" height="2048" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_3083.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_3083.JPEG 1000w, https://mellondev.net/content/images/2020/04/IMG_3083.JPEG 1536w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_3093.JPEG" width="1536" height="2048" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_3093.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_3093.JPEG 1000w, https://mellondev.net/content/images/2020/04/IMG_3093.JPEG 1536w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_3096.JPEG" width="2000" height="1500" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_3096.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_3096.JPEG 1000w, https://mellondev.net/content/images/size/w1600/2020/04/IMG_3096.JPEG 1600w, https://mellondev.net/content/images/2020/04/IMG_3096.JPEG 2048w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Dual LG 34&quot; Ultra-wide Curved monitors wall mounted</figcaption></figure><p>The adjustable arms are excellent, very sturdy and there&apos;s plenty of adjustment for positioning the monitors just right. &#xA0;I did have some trouble with the VESA mount and the LG monitors. &#xA0;The fixing plate that comes with the arms covers the ports on the monitor and I ended up having to cut some of the metal plate away so I could plug in my HDMI and power. </p><p>To power the monitors I needed some extra long power cables, the cables that came with the monitor were quite long but just not quite long enough to feed through the wall and under the desk.</p><ul><li><a href="https://amzn.to/2SKPfDQ">LG 34&quot; Ultrawide Curved monitor</a></li><li><a href="https://amzn.to/3cdnonJ">Adjustable tilt/swivel wall mount arms</a></li><li><a href="https://amzn.to/2L9E3fQ">3m Clover leaf power cable for monitors</a></li><li><a href="https://amzn.to/2Lk74pv">5m HDMI cable</a></li></ul><h2 id="the-computer">The Computer</h2><p>This office build all started out when I decided to build my own custom watercooled PC. &#xA0;I always loved the look of the Thermaltake Core P5 open case which could be wall mounted. </p><p>My previous PC was a standard dell workstation which I had for 7 years. &#xA0;I have built PC&apos;s in the past and wanted to custom build this one and give myself a challenge. &#xA0;A challenge it was too, I ended up using around 3m of tubing while learning how to do the bends. &#xA0;I wont be going into too much detail on the PC build as i&apos;m hoping to create a follow on post with full details of the build.</p><blockquote>Hard line tube bending is difficult, if its your first time get some spare tubing your going to need it :)</blockquote><p>Below are some pics of the PC build in its various stages, including my leak testing which amazingly went very well, not a single leak</p><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/2FE51807-D428-4FF2-ADE1-E05B4236528B.jpg" width="1440" height="1439" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/2FE51807-D428-4FF2-ADE1-E05B4236528B.jpg 600w, https://mellondev.net/content/images/size/w1000/2020/04/2FE51807-D428-4FF2-ADE1-E05B4236528B.jpg 1000w, https://mellondev.net/content/images/2020/04/2FE51807-D428-4FF2-ADE1-E05B4236528B.jpg 1440w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/10E0CC6D-7D16-430F-8FA1-586FC540FB45.jpg" width="1440" height="1439" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/10E0CC6D-7D16-430F-8FA1-586FC540FB45.jpg 600w, https://mellondev.net/content/images/size/w1000/2020/04/10E0CC6D-7D16-430F-8FA1-586FC540FB45.jpg 1000w, https://mellondev.net/content/images/2020/04/10E0CC6D-7D16-430F-8FA1-586FC540FB45.jpg 1440w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/22C89E91-5452-474F-BE58-92D134BDC371.jpg" width="2000" height="2000" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/22C89E91-5452-474F-BE58-92D134BDC371.jpg 600w, https://mellondev.net/content/images/size/w1000/2020/04/22C89E91-5452-474F-BE58-92D134BDC371.jpg 1000w, https://mellondev.net/content/images/size/w1600/2020/04/22C89E91-5452-474F-BE58-92D134BDC371.jpg 1600w, https://mellondev.net/content/images/size/w2400/2020/04/22C89E91-5452-474F-BE58-92D134BDC371.jpg 2400w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/2221066E-5CBA-4D71-80F0-6CE5D45AC42B.jpg" width="2000" height="2000" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/2221066E-5CBA-4D71-80F0-6CE5D45AC42B.jpg 600w, https://mellondev.net/content/images/size/w1000/2020/04/2221066E-5CBA-4D71-80F0-6CE5D45AC42B.jpg 1000w, https://mellondev.net/content/images/size/w1600/2020/04/2221066E-5CBA-4D71-80F0-6CE5D45AC42B.jpg 1600w, https://mellondev.net/content/images/size/w2400/2020/04/2221066E-5CBA-4D71-80F0-6CE5D45AC42B.jpg 2400w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0690.JPEG" width="1536" height="2048" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0690.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0690.JPEG 1000w, https://mellondev.net/content/images/2020/04/IMG_0690.JPEG 1536w" sizes="(min-width: 720px) 720px"></div></div></div></figure><h3 id="wall-mounted-pc">Wall mounted PC</h3><p>Wall mounting the PC was one of my main goals when I set out on this journey and was all part of the clean desk setup vision. &#xA0;The cables needed to be hidden and I wanted the PC to look like it was floating above the desk. &#xA0;</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0918-1.JPEG" width="1536" height="2048" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0918-1.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0918-1.JPEG 1000w, https://mellondev.net/content/images/2020/04/IMG_0918-1.JPEG 1536w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0921.JPEG" width="1536" height="2048" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0921.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0921.JPEG 1000w, https://mellondev.net/content/images/2020/04/IMG_0921.JPEG 1536w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0958.JPEG" width="1536" height="2048" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0958.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0958.JPEG 1000w, https://mellondev.net/content/images/2020/04/IMG_0958.JPEG 1536w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0961.jpg" width="2000" height="1500" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0961.jpg 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0961.jpg 1000w, https://mellondev.net/content/images/size/w1600/2020/04/IMG_0961.jpg 1600w, https://mellondev.net/content/images/2020/04/IMG_0961.jpg 2016w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Wall mounted watercooled PC with ThermalTake core P5 case</figcaption></figure><h3 id="peripherals">Peripherals</h3><p>After spending so much effort hiding the cables for the monitors and PC I had to go with a wireless keyboard and mouse. &#xA0;The MX Master 3 mouse was a no brainer for me and the new Logitech G915 keyboard looked good but very expensive, but I thought it will be used every day so the cost is justified.</p><p>I used to spend most nights coding listening to music through my headphones due to my home office been within the main house, but now I have a separate office I can get some decent speakers. &#xA0;After lots of research I decided on the Audioengine A2+ wireless with an Audioengine S8 subwoofer. &#xA0;The speakers sound and look great.</p><p>I really enjoy watching tutorial videos online and have always wanted to at some point share my knowledge and create my own videos. &#xA0;So as this is my ultimate home office I decided to get equipped ready to start producing some videos. For this I needed a decent mic, camera and webcam. &#xA0;For the mic I went for the Rode NT-USB mic along with the Rode boom arm. &#xA0;For the camera I chose the Cannon EOS 200D camera with a rode compact mic and a Joby gorilla tripod which is excellent. &#xA0;The tripod legs can be wrapped around anything and can also be arranged to be used as a selfie stick. &#xA0;I also needed a webcam to use for video conferencing and streaming going forward so chose the Logitech C920 HD Pro.</p><h3 id="build-specs">Build Specs</h3><ul><li><a href="https://amzn.to/2YHNJq8">Aorus Ultra motherboard</a></li><li><a href="https://amzn.to/2Wz4jpl">Intel i9-9900k</a></li><li><a href="https://amzn.to/2YG5lT9">Samsung 970 Evo Plus 1TB</a></li><li><a href="https://amzn.to/3ftYiTl">64gb Corsair Vengeance RGB Pro</a></li><li><a href="https://amzn.to/3bd6QuK">Aorus GeForce RTX 2080 Super Waterforce</a></li><li><a href="https://amzn.to/2WbQX3y">Thermaltake Core P5 Tempered Glass Case</a></li><li><a href="https://amzn.to/2xKiH5O">Thermaltake RGB watercooling </a>(pump, CPU block, radiator, flow indicator)</li></ul><h3 id="peripherals-1">Peripherals</h3><ul><li><a href="https://amzn.to/2yy2i4E">Logitech G915 wireless RGB keyboard</a></li><li><a href="https://amzn.to/35DWv9N">Logitech MX master 3 mouse</a></li><li><a href="https://amzn.to/2A7Au7N">AudioEngine A2+ wireless speakers</a></li><li><a href="https://amzn.to/35CdhX7">AudioEngine S8 subwoofer</a></li><li><a href="https://amzn.to/2L6m5ef">Rode NT-USB mic</a></li><li><a href="https://amzn.to/2YFzLVy">Rode swivel mount boom arm</a></li><li><a href="https://amzn.to/3ccDSwb">Logitech C920 HD Pro webcam</a></li><li><a href="https://amzn.to/2zfxWnB">Cannon EOS 200D</a></li><li><a href="https://amzn.to/3bhQMYM">Rode VideoMicro compact on camera mic</a></li><li><a href="https://amzn.to/35KgYdd">Joby gorilla tripod</a></li></ul><hr><p>Checkout my Instagram for more photos and progress on the setup</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.instagram.com/mellondev/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">@mellondev &#x2022; Instagram photos and videos</div><div class="kg-bookmark-description">Follow mellondev&#x2019;s Instagram account to see all 27 of their photos and videos.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.instagram.com/static/images/ico/favicon-192.png/68d99ba29cc8.png" alt="Home Office"><span class="kg-bookmark-author">Instagram</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://scontent-lht6-1.cdninstagram.com/v/t51.2885-19/91340150_3072583696114579_3743848585909764096_n.jpg?_nc_ht=scontent-lht6-1.cdninstagram.com&amp;_nc_ohc=xE6BR0jqj8EAX9Vy5cA&amp;oh=23ab538b4b13dfbcdbe5400756c56f1e&amp;oe=5EC0D483" alt="Home Office"></div></a></figure><h2 id="final-pics">Final pics</h2><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0927.JPEG" width="1536" height="2048" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0927.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0927.JPEG 1000w, https://mellondev.net/content/images/2020/04/IMG_0927.JPEG 1536w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0929-1.JPEG" width="1536" height="2048" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0929-1.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0929-1.JPEG 1000w, https://mellondev.net/content/images/2020/04/IMG_0929-1.JPEG 1536w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0954.jpg" width="2000" height="1500" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0954.jpg 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0954.jpg 1000w, https://mellondev.net/content/images/size/w1600/2020/04/IMG_0954.jpg 1600w, https://mellondev.net/content/images/2020/04/IMG_0954.jpg 2016w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0959.JPEG" width="1536" height="2048" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0959.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0959.JPEG 1000w, https://mellondev.net/content/images/2020/04/IMG_0959.JPEG 1536w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0971-2.jpg" width="2000" height="1500" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0971-2.jpg 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0971-2.jpg 1000w, https://mellondev.net/content/images/size/w1600/2020/04/IMG_0971-2.jpg 1600w, https://mellondev.net/content/images/2020/04/IMG_0971-2.jpg 2016w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0936.JPEG" width="2000" height="1500" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0936.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0936.JPEG 1000w, https://mellondev.net/content/images/size/w1600/2020/04/IMG_0936.JPEG 1600w, https://mellondev.net/content/images/2020/04/IMG_0936.JPEG 2048w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0938-1.JPEG" width="2000" height="1500" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0938-1.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0938-1.JPEG 1000w, https://mellondev.net/content/images/size/w1600/2020/04/IMG_0938-1.JPEG 1600w, https://mellondev.net/content/images/2020/04/IMG_0938-1.JPEG 2048w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://mellondev.net/content/images/2020/04/IMG_0945.JPEG" width="2000" height="1500" loading="lazy" alt="Home Office" srcset="https://mellondev.net/content/images/size/w600/2020/04/IMG_0945.JPEG 600w, https://mellondev.net/content/images/size/w1000/2020/04/IMG_0945.JPEG 1000w, https://mellondev.net/content/images/size/w1600/2020/04/IMG_0945.JPEG 1600w, https://mellondev.net/content/images/2020/04/IMG_0945.JPEG 2048w" sizes="(min-width: 720px) 720px"></div></div></div></figure>]]></content:encoded></item></channel></rss>