<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: James Eastham</title>
    <description>The latest articles on DEV Community by James Eastham (@jeastham1993).</description>
    <link>https://dev.to/jeastham1993</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F194720%2F64a58397-cd90-47c1-9264-915cc702187d.jpeg</url>
      <title>DEV Community: James Eastham</title>
      <link>https://dev.to/jeastham1993</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jeastham1993"/>
    <language>en</language>
    <item>
      <title>On Choosing Your Complexity</title>
      <dc:creator>James Eastham</dc:creator>
      <pubDate>Tue, 17 Dec 2024 08:00:00 +0000</pubDate>
      <link>https://dev.to/jeastham1993/on-choosing-your-complexity-1k6n</link>
      <guid>https://dev.to/jeastham1993/on-choosing-your-complexity-1k6n</guid>
      <description>&lt;p&gt;Software is complex. I don't think that's a controversial statement. Building, testing, observing, scaling, failing, evolving. And that's before you try to understand what it is your users want the system to do.&lt;/p&gt;

&lt;p&gt;At times, it can be a bit demoralizing can't it? Constantly fighting against this ever changing, ever growing challenge of a system that is getting more and more complex. You think back to the days when you built a "hello world" application to learn a new concept and you wonder where all this complexity came from.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Simple can be harder than complex: You have to work hard to get your thinking clean to make it simple. But it's worth it in the end because once you get there, you can move mountains. - &lt;em&gt;Steve Jobs&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Reading Steve Jobs' biography (which is an excellent read by the way) one of my biggest takeaways is simplicity. One of the reasons Apple became so successful is the relentless focus on simplicity.&lt;/p&gt;

&lt;p&gt;Take the well-told story of Job's second coming at Apple. On his return, the product lines had grown massively. To the point Apple's own teams couldn't explain which Mac to pick. Internally, teams thought they were meeting a user needs when actually they were confusing customers with so many choices.&lt;/p&gt;

&lt;p&gt;When Job's came back, he changed the entire product line to just have 4 products. Yep, just 4. 2 desktop computers and 2 portable devices. An obvious example of simplification, but it had the right effect.&lt;/p&gt;

&lt;p&gt;When you're trying to change an entire industry, you are already dealing with lots of complexity. Changing human behaviors, introducing a new way of interacting with technology. This is &lt;strong&gt;essential complexity&lt;/strong&gt;. Introducing 10x more product lines than you actually need, that is a form of &lt;strong&gt;accidental complexity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What does this mean for you dear reader? Many of you will be working on new systems. New systems that will require a change in human behavior. Many of you are likely operating in a highly complex business domain where the problem space itself is highly complex.&lt;/p&gt;

&lt;p&gt;Every single software system that exists has a certain level of &lt;strong&gt;essential complexity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Take &lt;a href="https://github.com/jeastham1993/PlantBasedPizza" rel="noopener noreferrer"&gt;Plant Based Pizza&lt;/a&gt;, the backend for a pizza restaurant. Whilst not the most complex of domains, there is still a decent amount of complexity to think about. Taking orders, managing stock, managing staff, taking payments, refunding payments, preparing orders, organizing delivery drivers and getting the order delivered to the correct address.&lt;/p&gt;

&lt;p&gt;And that's just considering the happy path. What if a payment fails? Or an order is ready but there aren't any delivery drivers available.&lt;/p&gt;

&lt;p&gt;Think about the systems you are working on, they will inevitably have their own essential complexity.&lt;/p&gt;

&lt;p&gt;Unfortunately for us, this complexity can't be removed. Much like Apple trying to change an entire industry, you can't get around this complexity. Trying to "simplify" this complexity can seem like a challenge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accidental complexity&lt;/strong&gt; though, that's something you do have some agency over.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accidental Complexity
&lt;/h2&gt;

&lt;p&gt;Accidental complexity is the complexity that comes from the choices you make. The complexity that &lt;strong&gt;you&lt;/strong&gt; (or your company) has control over.&lt;/p&gt;

&lt;p&gt;Every technology choice you make will change the amount of accidental complexity of your system.&lt;/p&gt;

&lt;p&gt;When &lt;a href="https://github.com/jeastham1993/PlantBasedPizza" rel="noopener noreferrer"&gt;Plant Based Pizza&lt;/a&gt; first started, they focused on delivering value quickly. At that time, there was a team of 5 building the backend. They built a monolithic application with a Postgres database to support all the different business use cases.&lt;/p&gt;

&lt;p&gt;A single running process and a simple, flexible and well understood database technology == simplicity!&lt;/p&gt;

&lt;p&gt;Of course, once they built the application they needed to run it somewhere. One of the team had been reading about Kubernetes recently and got the rest of the team incredibly excited about building a shiny new Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;They created a cluster, deployed some virtual machines instances as the underlying compute and used a managed database service for providing the Postgres functionality. Great, we are live.&lt;/p&gt;

&lt;p&gt;This isn't the part of the story where I shit on Kubernetes. It's merely an example of the complexity you've just accidentally introduced into your system. Think about all the things you need to think about running a Kubernetes cluster:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scaling nodes&lt;/li&gt;
&lt;li&gt;Operating nodes&lt;/li&gt;
&lt;li&gt;Securing nodes&lt;/li&gt;
&lt;li&gt;Dealing with node failures&lt;/li&gt;
&lt;li&gt;Upgrading the cluster&lt;/li&gt;
&lt;li&gt;Securing the cluster&lt;/li&gt;
&lt;li&gt;Dealing with the cluster failing&lt;/li&gt;
&lt;li&gt;Scaling your application&lt;/li&gt;
&lt;li&gt;Dealing with application failures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's before you consider the various 3rd party tools you'll need to deploy inside the cluster and keep up to date.&lt;/p&gt;

&lt;p&gt;Let's consider an alternative universe where the team made a different decision.&lt;/p&gt;

&lt;p&gt;The team still packaged the monolithic backend application as a container image, and still used a Postgres database. They made that decision for simplicity (containers are well understood) and portability (containers can run anywhere, as can Postgres).&lt;/p&gt;

&lt;p&gt;Instead of spinning up a Kubernetes cluster though, they used a managed container orchestrator. Something like Amazon ECS (with Fargate), Azure Container Apps or Google Cloud Run. They kept the Postgres database running in a managed cloud database service.&lt;/p&gt;

&lt;p&gt;Let's look at the complexity you've just introduced:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scaling your application&lt;/li&gt;
&lt;li&gt;Dealing with application failures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A shorter list, right? Using a managed container orchestrator means you shift a whole bunch of that accidental complexity on to the cloud provider. Operating nodes? Operating clusters? Dealing with node failures? That's the cloud providers responsibility now.&lt;/p&gt;

&lt;p&gt;There's a trade off here with cost of course, and I talked about that recently in this post &lt;a href="//./on-factors-of-modern-compute.md"&gt;on the factors of modern compute&lt;/a&gt;].&lt;/p&gt;

&lt;p&gt;Continuing this tale, we fast forward 12 months. Plant Based Pizza has grown, and there are now 50 developers across 5 different teams. All teams are still contributing to that same monolithic code base, and are getting in each others way.&lt;/p&gt;

&lt;p&gt;This is a form of accidental complexity! The complexity that comes from the decision to grow teams. Complexity you now need to deal with.&lt;/p&gt;

&lt;p&gt;Microservices are a reaction to organization challenges. Microservices are a solution to a socio-technical problem. They aren't a solution to a technology problem.&lt;/p&gt;

&lt;p&gt;So the Plant Based Pizza team decides to start to break their application down into microservices. As you might expect, microservices introduce their own complexity. And believe me, microservices introduce a lot of technology complexity.&lt;/p&gt;

&lt;p&gt;At this point, there is no good reason to change the complexity of your underlying compute. You are already introducing enough additional complexity by choosing microservices, introducing another layer of infrastructure complexity probably won't help you too much.&lt;/p&gt;

&lt;p&gt;Speeding forward in time again, Plant Based Pizza has now been refactored to microservices. These microservices are communicating with asynchronous events to make them as loosely coupled as possible. And you're still running the applications (packaged as containers) on serverless compute. You still don't have a good reason to introduce the complexity of Kubernetes.&lt;/p&gt;

&lt;p&gt;What accidental complexity does the system have?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scaling the applications&lt;/li&gt;
&lt;li&gt;Dealing with application failures&lt;/li&gt;
&lt;li&gt;Eventual consistency across different microservices&lt;/li&gt;
&lt;li&gt;Service to service communication&lt;/li&gt;
&lt;li&gt;Observability (it's hard in asynchronous systems)&lt;/li&gt;
&lt;li&gt;Governance and evolving the system safely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Slightly more complexity, but still not crazy amounts right? And all this complexity is something you can have direct control over as a developer.&lt;/p&gt;

&lt;p&gt;If you choose to run your application on Kubernetes you still have this complexity to deal with, as well as all the complexities that come from Kubernetes.&lt;/p&gt;

&lt;p&gt;Again, this isn't a post written to bash Kubernetes. It has a place in the world. But you probably don't need it for your applications.&lt;/p&gt;

&lt;p&gt;And if you are building with Kubernetes, you can still apply this lens of accidental complexity. Does every single developer inside your company need to understand Kubernetes? Do they need to understand Helm charts and YAML files. Or can you build your Kubernetes cluster in a way that means developers only care about shipping container images. This is, of course, platform engineering.&lt;/p&gt;

&lt;p&gt;When I was at AWS I worked with a company (that I can't name) doing this. They were building an abstraction layer on top of Kubernetes so that developers could just ship container images and not worry about anything else. To add complexity, this abstraction layer also spanned multiple cloud providers.&lt;/p&gt;

&lt;p&gt;Whilst it was an interesting project to work on, it wasn't for the business. They had put an insane amount of money, developer time and operational responsibility into building a service that wasn't even nearly as feature complete as Amazon ECS or Azure Container Apps. They even had a side by side comparison chart and the custom built service wasn't anywhere near.&lt;/p&gt;

&lt;p&gt;All of that, to deploy business applications... To put it bluntly what a massive waste of time and effort.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You need Kubernetes if your ability to dynamically scale and manage infrastructure is a core differentiator for your business.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The takeaway from this rambling post is to think about the decisions you make and the complexity that introduces.&lt;/p&gt;

&lt;p&gt;Focus on building stateless, portable, containerized applications. Focus on structuring your application code to separate infrastructure concerns from business logic (ports and adapters style). Focus on shipping those applications in an operationally light way as possible.&lt;/p&gt;

&lt;p&gt;Reduce your operational/infrastructure complexity by adopting managed platforms and shifting that responsibility onto the cloud provider. Cost is of course a consideration here. The line item cost of running Fargate is more than a comparable EC2 instance. But that doesn't take into account the human cost required to run EC2.&lt;/p&gt;

&lt;p&gt;You can see this line of thinking in action with the fact all the cloud providers now have some form of 'Kubernetes auto'. Where you can run Kubernetes, and then pay your cloud provider extra to take a chunk of the operational overhead.&lt;/p&gt;

&lt;p&gt;If you focus on stateless, portable containers this compute conversation becomes largely irrelevant. Run your application in a place that makes sense right now, and you are ready to deal with future changes.&lt;/p&gt;

&lt;p&gt;As a thought exercise, think about the accidental complexity you have in your system today? What complexity are you dealing with that isn't related to the business problem you are trying to solve. And importantly, what could you do to reduce it?&lt;/p&gt;

</description>
      <category>complexity</category>
      <category>architecture</category>
      <category>softwaredevelopment</category>
      <category>cloud</category>
    </item>
    <item>
      <title>The Essential Art of Sustainable Software Architecture</title>
      <dc:creator>James Eastham</dc:creator>
      <pubDate>Thu, 27 Apr 2023 18:44:57 +0000</pubDate>
      <link>https://dev.to/jeastham1993/the-essential-art-of-sustainable-software-architecture-23he</link>
      <guid>https://dev.to/jeastham1993/the-essential-art-of-sustainable-software-architecture-23he</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Sustainability consists of &lt;strong&gt;fulfilling&lt;/strong&gt; the &lt;strong&gt;needs&lt;/strong&gt; of &lt;strong&gt;current generations without compromising&lt;/strong&gt; the &lt;strong&gt;needs&lt;/strong&gt; of &lt;strong&gt;future generations&lt;/strong&gt;, while ensuring a balance between &lt;strong&gt;economic&lt;/strong&gt; growth, &lt;strong&gt;environmental&lt;/strong&gt; care and &lt;strong&gt;social&lt;/strong&gt; well-being.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sustainability; a word we hear an awful lot and one that means different things to different people. What does it  mean to be sustainable? The definition above sums it up for me, fulfil the needs of the present without compromising the future.&lt;/p&gt;

&lt;p&gt;When we think about sustainability, our minds jump to climate change, making sure we re-use our shopping bags and making sure we recycle our plastic bottles. These are noble causes, as technologists, we have the &lt;strong&gt;potential&lt;/strong&gt; to make a &lt;strong&gt;bigger impact&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Software is taking over the world. Every single organisation on the planet is now considering itself a technology company.&lt;/p&gt;

&lt;p&gt;This gives us a huge opportunity to have a tremendous impact. Huge portions of the world are using software every single day. Is it not our responsibility to ensure that the software we architect meets the needs of the present, whilst optimising for the future?&lt;/p&gt;

&lt;p&gt;What makes up the art of sustainable software architecture, balanced across the environmental, economic and social pillars?&lt;/p&gt;

&lt;h2&gt;
  
  
  Environmental Software Architecture
&lt;/h2&gt;

&lt;p&gt;The environmental pillar is imperative, and the most obvious when we think about sustainability. To create a more sustainable planet, and aim to mitigate climate change. There is a single word that springs to mind when I think about the environmental impact of our architectures:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Efficiency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The ability to do more with as few resources as possible. The art of having the right resource, in the right place, at just the right time. With some of the recent innovations in software development, efficiency is a practical thing to implement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Leveraging the cloud for efficiency
&lt;/h3&gt;

&lt;p&gt;A report by &lt;a href="https://www.spglobal.com/marketintelligence/en/news-insights/research/carbon-reduction-in-cloud"&gt;S&amp;amp;P Global Market Intelligence&lt;/a&gt; discusses the impact of moving workloads to the cloud. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Simply moving to using green energy is not going to deliver the desired — or the best — carbon reduction. The best place to go from a carbon reduction perspective is the cloud."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The report estimates that a combination of more up-to-date equipment and a better utilised fleet of hardware amounted to an 85% saving in energy usage. This is before considering the fact that many &lt;a href="https://sustainability.aboutamazon.co.uk/environment/the-cloud?energyType=true"&gt;cloud providers are moving their data centres to use 100% renewable energy&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architect for Efficiency
&lt;/h3&gt;

&lt;p&gt;Many of you have heard of the cloud, and are likely already running some kind of workload in the cloud. But once in the cloud, how can we architect for efficiency? Thankfully, the architecture patterns that are the most efficient also are the most optimal ways to build modern software.&lt;/p&gt;

&lt;p&gt;Serverless-first, event driven architectures are a prevalent architectural pattern when building cloud native systems. Remember, one principle we are looking to optimise our systems is to do more with less. A system that scales to zero when it’s not in use, and reacts to events as they happen... What could’ve more optimised than that?&lt;/p&gt;

&lt;p&gt;Let's unpack the two terms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Serverless first: A mindset when building software that defaults to leveraging serverless compute options like AWS Lambda. Shifting the operational burden on to the cloud provider. This enables the cloud provider to be efficient with the underlying compute as they can shift workloads around to meet demand.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Event Driven Architecture: A pattern in which business events drive functionality, coupled with an asynchronous first approach. A system which reacts to events as they happen, and defaults to running things asynchronously, provides ample opportunity to optimise for sustainability.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, a serverless event driven architecture may not always be the most optimal or even the most practical. I'm looking at all you GPU compute heavy folks out there. That said, it's still possible to architect for efficiency.&lt;/p&gt;

&lt;p&gt;Defaulting to a single type of compute everywhere in your system is an anti-pattern. Choose the &lt;strong&gt;right compute&lt;/strong&gt; for your &lt;strong&gt;use case&lt;/strong&gt; and optimise accordingly.&lt;/p&gt;

&lt;p&gt;If that means running big GPU intensive EC2 instances, then go for it, but consider how you could be more efficient with the compute that you have.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think asynchronous&lt;/strong&gt; first, default to using managed services and take intentional step outs away from this pattern where required.  &lt;/p&gt;

&lt;h3&gt;
  
  
   Code for Efficiency
&lt;/h3&gt;

&lt;p&gt;Code is a fundamental part of any software system. There is so much nuance in an individual business use case that hand written, custom code is inevitable. Many of us (me included) default to familiarity. Debating the merits of a language based on syntax, performance and legibility (I’m looking at you Python).&lt;/p&gt;

&lt;p&gt;Something I rarely hear mentioned in programming languages, is the environmental impact. A group of extremely smart people out of various universities in &lt;a href="https://greenlab.di.uminho.pt/wp-content/uploads/2017/10/sleFinal.pdf"&gt;Portugal have ran this analysis for us&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The report linked above analyses 27 well-known software languages and monitors the performance using 10 different programming problems, expressed in each of the languages. An interesting observation from this paper is that:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Compiled languages tend to be, as expected, the fastest and most energy efficient ones. On average, compiled languages consumed 120J to execute the solutions, while for virtual machine and interpreted languages this value was 576J and 2365J, respectively. This tendency can also be observed for execution time, since compiled languages took 5103ms, virtual machine languages took 20623ms, and interpreted languages took 87614ms (on average)."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I find this even more interesting considering how prevalent interpreted languages like Python and NodeJS are in our modern software world.&lt;/p&gt;

&lt;p&gt;Unsurprisingly, the top 5 languages for both energy efficiency and execution time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;C&lt;/li&gt;
&lt;li&gt;Rust&lt;/li&gt;
&lt;li&gt;C++&lt;/li&gt;
&lt;li&gt;Ada&lt;/li&gt;
&lt;li&gt;Java&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And the bottom 5 for energy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Perl&lt;/li&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;Ruby&lt;/li&gt;
&lt;li&gt;JRuby&lt;/li&gt;
&lt;li&gt;Lua&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And execution time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Lua&lt;/li&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;Perl&lt;/li&gt;
&lt;li&gt;Ruby&lt;/li&gt;
&lt;li&gt;Typescript
&lt;em&gt;See, my looks at Python weren't completely off the mark&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Quick side note, Rust has also ranked as the most beloved programming language in the Stack Overflow developer survey for 7 years running (as of the 2022 survey).&lt;/p&gt;

&lt;p&gt;Now I'm not suggesting you all go away to learn Rust (although it's a lot of fun) and re-write your entire applications. We will see more on this when we move on to the &lt;strong&gt;social&lt;/strong&gt; pillar of sustainable architecture. But understand the efficiencies of each language is important.&lt;/p&gt;

&lt;p&gt;Maybe you don't re-write your entire application, but you re-write the most commonly used pieces of functionality to use a more efficient language. Taking Rust as a specific example, combined with AWS serverless compute, this is also likely to save you money as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Social Pillar of Software Architecture
&lt;/h2&gt;

&lt;p&gt;When I first looked at the pillars of sustainability, the social one made me stop and think. There is the obvious social impact of software; building software to solve the world's hardest problems. But can be dive a little deeper into that, what about the day to day social impacts of the people building the software?&lt;/p&gt;

&lt;p&gt;I'm sure many of us have worked on applications where the code was an unruly big ball of complicated mud. Equally, I'm sure some of of us have worked inside code bases that are a perfectly modularised thing of beauty.&lt;/p&gt;

&lt;p&gt;Not all of us can work on software that is 'changing the world', but we can all aim to build software that considers the needs of future generations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enjoyable to work on
&lt;/h3&gt;

&lt;p&gt;One of the most important characteristics of social software is a code base that is enjoyable to work on. I want to go to work and have fun solving problems, instead of battling with complexity, un-necessary abstractions and hard to read code.&lt;/p&gt;

&lt;p&gt;The boy scout rule is an excellent mantra to use here:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Always leave the code better than you found it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Simple, easy to remember and forces you to think about continuously improving code. Maybe this is as simple as re-naming a variable to be more expressive. Maybe it's adding additional services and functions to make the code more intentional.&lt;/p&gt;

&lt;p&gt;A rule of thumb I try to follow (and often fail at) is to write code that reads like a story. If you’re using a compiled language, using long easy to read variable names is irrelevant because the compiler will compress them down.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;currentIterator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;currentProductIndex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;maxProducts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;currentProductIndex&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;currentProduct&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;allProducts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;currentProductIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I know which I prefer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduce cognitive load
&lt;/h3&gt;

&lt;p&gt;Cognitive load is devastating to a software developer. Our brains can only hold so much information in our head at a time. When working on tasks, it functions more like a stick of RAM than an SSD.&lt;/p&gt;

&lt;p&gt;Reducing the cognitive load of a system enables your fellow developers to make more informed decisions about functionality. If I can consume a library with a well defined API taht abstracts away a complex task, that reduces my cognitive load. Giving me more mental cycles to focus on the problem at hand.&lt;/p&gt;

&lt;p&gt;Defaulting to a modular design, whether that be a modular monolith or microservices, reduces this cognitive burden. Especially when coupled with domain driven design, defined bounded contexts and organising teams around features and not technical domains.&lt;/p&gt;

&lt;p&gt;If I'm a developer working on the product microservice, I can become fully embedded in the world of products within the organisation. I need to get some customer information, well the customer team has a well defined API interface with excellent documentation. They even produced an SDK for communication, excellent! Cognitive load removed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fast Feedback
&lt;/h3&gt;

&lt;p&gt;Dopamine; the neurochemical responsible for almost all of our modern addictions. That little burst of pleasure you get when you get a new notification... That’s dopamine in action. It’s not all bad though. We can leverage dopamine in positive ways as well.&lt;/p&gt;

&lt;p&gt;Fast feedback loops within the application, typically with a well defined set of test cases, help enable this reinforcement. Imagine working within a code base and continuously running the test cases seeing all of them green icons light up. You can feel that dopamine hit already, can’t you?&lt;/p&gt;

&lt;p&gt;These fast feedback loops also help enable mastery of the domain and language. A core component of mastering anything is quickly being able to course correct.&lt;/p&gt;

&lt;p&gt;Tests light up green = dopamine hit&lt;/p&gt;

&lt;p&gt;Tests go red = an opportunity to increase your mastery&lt;/p&gt;

&lt;h3&gt;
  
  
  Extensible &amp;amp; Observable
&lt;/h3&gt;

&lt;p&gt;The last topic I wanted to touch on around the social impact is that of extensibility and observability. I want to work within a system in which I can easily extend it to add new functionality, and observe/debug it when things go wrong.&lt;/p&gt;

&lt;p&gt;Some of the most frustrating times in my career have been through trying to debug a problem that has little to no logs or tracing.&lt;/p&gt;

&lt;p&gt;Building with extensibility and observability in mind will impact the needs of future developers, meeting our commitment to the social pillar.&lt;/p&gt;

&lt;p&gt;The extensibility angle ties nicely in with our serverless-first, event driven mindset. An event driven system is extensible. If your system behaviour is driven by a set of business events shared between bounded contexts through some kind of central event stream, hooking into that event stream to add new functionality becomes incredibly easy. Coupling that central event stream with a schema registry and you are in a fantastically extensible place.&lt;/p&gt;

&lt;p&gt;The observability angle applies to much more than the social component. Observability will help us environmentally and economically (you can’t measure what you can’t improve), as well as socially (understanding what the heck is going on in a system).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The mindset of the sustainable architect is to be serverless-first, asynchronous and event driven by nature with observability as a fundamental tenant.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Economic Pillar of Sustainable Architecture
&lt;/h2&gt;

&lt;p&gt;If the social pillar made me pause for thought, this one had me stumped for a little whilse. The definition of the economic pillar is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This pillar is based on companies’ ability to contribute to economic development and growth.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As architects and engineers, we have little say in our organisations’ wider policies on contributing to economic development and growth. So what does economic development and growth mean within the context of software?&lt;/p&gt;

&lt;p&gt;Within this pillar, I see the practical patterns &amp;amp; steps you can take as an architect to contribute to the development and growth of your software system sustainably. There are many ties with the environmental pillar here, with &lt;strong&gt;efficiency&lt;/strong&gt; being a fundamental component. So let's consider the economics of the different system components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compute
&lt;/h3&gt;

&lt;p&gt;We’ve discussed compute already, defaulting to a serverless-first mindset. But there are several additional patterns and practices to help enable sustainable use of compute.&lt;/p&gt;

&lt;p&gt;The first is to &lt;strong&gt;optimise for stateless&lt;/strong&gt; compute. Aim for your application layer to hold little to no state. It helps to enable the event driven, reactive compute we are looking for. If your application requires in-memory state, then you restrict the type of compute you can use, as well as how you and your cloud provider can shift that compute around.&lt;/p&gt;

&lt;p&gt;Holding an element of data in memory may be useful from a performance perspective, caching for example, but you should not rely on it.&lt;/p&gt;

&lt;p&gt;Second, and related to the earlier point, optimise the actual compute itself. This may mean using more optimised chipsets (x86 vs ARM). Or it may mean optimising the type of compute you have. If you’re within the serverless world, this may mean optimising the memory allocation of your function to get the optimal performance vs memory balance. If you're in the physical server world, this may use the most optimal instance type (compute vs memory vs network optimised) for your specific use case.&lt;/p&gt;

&lt;p&gt;There is not a one size fits all in compute, it &lt;em&gt;always depends&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Networking / Data Transfer
&lt;/h3&gt;

&lt;p&gt;The environmental impact of networking and data transfer is difficult to quantify, as there are so many variables. That doesn’t mean we can’t be more economical though.&lt;/p&gt;

&lt;p&gt;Ask yourself the question, what is the minimal viable data I need to perform the task I have? The backend for front-end (BFF) pattern is excellent for this, alongside technologies like GraphQL. The aim here - ensure only useful data is transferred.&lt;/p&gt;

&lt;p&gt;Compression is another decision point that we will see again when we discuss storage. Many workloads, including almost every single one I have built, default to JSON as the data transfer medium. Understandably so. It’s human readable and relatively succinct. But actually, as we are passing data over the wire, can we not make this even more efficient?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://protobuf.dev"&gt;Protobuf&lt;/a&gt; and &lt;a href="https://msgpack.org/index.html"&gt;MessagePack&lt;/a&gt; are two such technologies specifically targeting efficient binary serialization, whilst still maintaining an element of schema and operability.&lt;/p&gt;

&lt;p&gt;Caching and data transfer distance are two concepts that go hand in hand. That is using content delivery networks (CDN) to get the data and compute as close to your users as possible. Caching data at the end, as well as running compute at the edge, reduces the network ‘cost’ of transferring the data.&lt;/p&gt;

&lt;p&gt;Caching is also valuable within our backend systems. If you are working within a micro-services architecture, it’s likely you have an element of communication across a network. Consider if you could cache external data within your own service to reduce the network transfer cost and secondarily reducing the compute load on your other systems (a big tick in the social pillar here as well).&lt;/p&gt;

&lt;p&gt;Micro-services architectures, coupled with event driven architecture, also bring up an important point of debate around event schema design. There is a probably a separate blog post within this topic, but determining what you include in your event is important. Events that are too sparse result in many calls back to the producer service to retrieve additional data. A ‘fat’ event results in a larger initial cost as it distributes the event to all the subscribers.&lt;/p&gt;

&lt;p&gt;That said, if we truly optimising for sustainability, my preference is for a fat event with a combination of JSON &amp;amp; Binary. We compress the main body of the payload into a binary format using a schema-based compression like Protobuf, with additional metadata in readable JSON. That strikes the right balance between a well-known schema, reduced network calls and an optimally sized payload as well as providing the ability for subscribers to filter data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"metadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"eventType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"orderCreated"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"filters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"orderValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;256.82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"geo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"customerAccountId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"deliveryType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PREMIUM"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"payload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;BINARY_DATA_GOES_HERE&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Storage &amp;amp; Analytics
&lt;/h3&gt;

&lt;p&gt;Many of the concepts around compression discussed above apply equally to storage. The most important architectural consideration here is to &lt;strong&gt;understand your data access patterns&lt;/strong&gt;. Regardless of the storage technology you are using, understanding how the data is going to be accessed is vital in understanding how you can optimise.&lt;/p&gt;

&lt;p&gt;For example, let’s consider the use of DynamoDB as a primary data store. Used in the most efficient way, access to DynamoDB uses a combination of partition and sort keys. Any other data stored against an item isn’t available to query.&lt;/p&gt;

&lt;p&gt;In this scenario, leverage technologies like Protobuf to serialize the main bulk record to binary and store that in a more efficient way. An additional benefit here, you’ll probably reduce your costs as well.&lt;/p&gt;

&lt;p&gt;Considering your access patterns will also enable you to optimise the performance of your queries, having the knock on effect of optimising your compute. It’s a wonderful sustainability flywheel we are getting ourselves into, isn’t it?&lt;/p&gt;

&lt;p&gt;The other consideration around the data itself is what you store and how long you store it for. Continuously ask yourself the question about the data being stored and if all of it is necessary. And if it is, how long do you need to keep it around for?&lt;/p&gt;

&lt;p&gt;And finally, think about your recovery point objective and recovery time objective. Do you really need to be taking full database backups of your product review database every 5 minutes and storing them for up to 3 years? Probably not. Much like the compute option, fit the technology to the use case. There is no such thing as a one size fits all storage solution.&lt;/p&gt;

&lt;p&gt;Now some of these ideas become less useful when considering analytics. A nicely compressed piece of binary data is excellent for our storage costs and retrieval efficiency, not so useful for running custom analytics queries. How can we be pro-active about our analytics workloads?&lt;/p&gt;

&lt;p&gt;Unlike the compute recommendation of being reactive, only running applications when required, for analytics I would suggest being much more pro-active. Specifically, transform your data into a format that is suited for custom queries. Extract, transform, load (ETL) jobs have been around for a long time. One of my first jobs was developing ETL jobs. Using these pro-active mechanisms for pre-formatting data ensures that when these complex, custom queries come in, they are working in data sorted in the most efficient way.&lt;/p&gt;

&lt;p&gt;If you combine this with scheduling your ETL jobs to run during low-carbon intensive periods, you can really push up the sustainability of your analytics workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Charachterize. Observe. Improve
&lt;/h2&gt;

&lt;p&gt;I wanted to close out with a very specific section on architectural thinking. All architectures comprise the architectural ‘-ilities’. These ilities are often at odds with each other and are at the core of the often heard phrase ‘it depends’.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance&lt;/li&gt;
&lt;li&gt;Scalability&lt;/li&gt;
&lt;li&gt;Availability&lt;/li&gt;
&lt;li&gt;Reliability&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Maintainability&lt;/li&gt;
&lt;li&gt;Flexibility&lt;/li&gt;
&lt;li&gt;Configurability&lt;/li&gt;
&lt;li&gt;Personalizability&lt;/li&gt;
&lt;li&gt;Usability&lt;/li&gt;
&lt;li&gt;Portability&lt;/li&gt;
&lt;li&gt;Conformance to standards&lt;/li&gt;
&lt;li&gt;Efficiency&lt;/li&gt;
&lt;li&gt;Responsiveness&lt;/li&gt;
&lt;li&gt;Interoperability&lt;/li&gt;
&lt;li&gt;Upgradability&lt;/li&gt;
&lt;li&gt;Auditability&lt;/li&gt;
&lt;li&gt;Transactionality&lt;/li&gt;
&lt;li&gt;Adminstrability&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Sustainability (my addition)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, it's difficult to combine performance and security. Increasing security typically means slower performance. If we consider sustainability to be a non-negotiable, that naturally makes some of the other ‘-ilities’ fall become clear as well. This is very much my opinion, but these are what I would consider the most important ilities for sustainability.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scalability&lt;/li&gt;
&lt;li&gt;Security (it's job zero, it makes it on to every list)&lt;/li&gt;
&lt;li&gt;Flexibility&lt;/li&gt;
&lt;li&gt;Usability&lt;/li&gt;
&lt;li&gt;Efficiency&lt;/li&gt;
&lt;li&gt;Interoperability&lt;/li&gt;
&lt;li&gt;Upgradability&lt;/li&gt;
&lt;li&gt;Auditability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once we have defined the charachterisitics we want our system to have, we can use &lt;a href="https://www.thoughtworks.com/en-gb/insights/articles/fitness-function-driven-development"&gt;fitness functions&lt;/a&gt; to ensure we have continual feedback for conformance to the core tenets of our system.&lt;/p&gt;

&lt;p&gt;For example, if scalability and efficiency are our core tenets to enable sustainability, we may write fitness functions to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monitor resource usage and how optimally we are consuming the resources we have allocated to a task&lt;/li&gt;
&lt;li&gt;Monitor how often we are using compute when there is no work to be done&lt;/li&gt;
&lt;li&gt;Ensure no individual piece of compute runs for longer than 100ms, and uses more than 128mb of memory&lt;/li&gt;
&lt;li&gt;Monitor scaling behaviour, and how quickly our system adapts to demand&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fitness functions should run both as triggers, as part of CICD, and continually as part of ongoing system monitoring.&lt;/p&gt;

&lt;p&gt;For all of that to work, observability is another non-negotiable '-ility' in all of our sustainable architectures. Generating trace and metrics data to understand our systems enables us to be data driven about how we optimise and gives our fitness functions something to work with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;If you’ve stuck with it this far, thanks for reading. If I’ve lost you, well, you’ve probably not got this far anyway, so this sentence is becoming irrelevant.&lt;/p&gt;

&lt;p&gt;Frankly, this post has been a bit of a brain dump. A coming together of my technical skills and my values to want to inspire change in the world.&lt;/p&gt;

&lt;p&gt;I truly believe that as software practitioners, we have immense potential to bring about genuine change in the world.&lt;/p&gt;

&lt;p&gt;Let that carry into everything you do.&lt;/p&gt;

&lt;p&gt;James&lt;/p&gt;

</description>
      <category>sustainability</category>
      <category>architecture</category>
      <category>development</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Learning Software Architecture? Start With Why!</title>
      <dc:creator>James Eastham</dc:creator>
      <pubDate>Fri, 07 Apr 2023 11:29:22 +0000</pubDate>
      <link>https://dev.to/jeastham1993/learning-software-architecture-start-with-why-18gj</link>
      <guid>https://dev.to/jeastham1993/learning-software-architecture-start-with-why-18gj</guid>
      <description>&lt;p&gt;61,698,933!&lt;/p&gt;

&lt;p&gt;That's the number of views (at the time of writing) &lt;a href="https://www.ted.com/talks/simon_sinek_how_great_leaders_inspire_action/no-comments"&gt;Simon Sinek's TED talk on 'How great leaders inspire action'&lt;/a&gt; has. A fundamental idea from the talk is the idea of a golden circle, and how we default to approaching things in the complete opposite way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5tal6p73a5c7d0opvu7i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5tal6p73a5c7d0opvu7i.png" alt="Image description" width="800" height="665"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Many of us approach things with an outside in approach. We start with what, take steps to work out how, and rarely worry too much about the why. This is a flawed approach. Starting with why, at least in business, explains your purpose and why you are doing the thing you are doing. People buy your why, not your what. Yet many businesses start with the what, and completely lose the sense of purpose. Your why drives the relationship with your customers.&lt;/p&gt;

&lt;p&gt;You might wonder how this short piece of business advice relates to how we teach software. But let's take this same golden circle, and apply it to how many of us try to learn about software architecture.&lt;/p&gt;

&lt;p&gt;People interested in software architecture often ask me which AWS service to learn first. Or which certification to take. Do I learn about serverless or containers? Event driven architecture or microservices? These areas are inevitably ones you will need to learn about at some point, but they are &lt;strong&gt;unlikely&lt;/strong&gt; to be the best the place to start.&lt;/p&gt;

&lt;p&gt;You are starting with &lt;strong&gt;what&lt;/strong&gt;! Instead, start with &lt;strong&gt;why&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting with Why
&lt;/h2&gt;

&lt;p&gt;Build your knowledge from &lt;a href="https://fs.blog/first-principles/"&gt;first principles&lt;/a&gt;. Technology iterates on itself quickly, and every iteration takes a problem seen previously and makes it better. These iterations stack on top of each other to give us the technology choices we have today.&lt;/p&gt;

&lt;p&gt;On-Premise servers -&amp;gt; Elastic Cloud Servers -&amp;gt; Containers -&amp;gt; Container Orchestrators -&amp;gt; Serverless&lt;/p&gt;

&lt;p&gt;The most famous saying in software architecture is &lt;strong&gt;it depends&lt;/strong&gt;. Taking a specific technology driven approach, to learning software architecture limits your 'it depends' to the specific niche of technology you are learning. If you have a hammer, everything looks like a nail. If you have a &lt;a href="https://aws.amazon.com/lambda/"&gt;Lambda&lt;/a&gt; function, everything looks like it's event driven.&lt;/p&gt;

&lt;p&gt;Instead of asking why exactly are we trying to hang this thing from the wall?&lt;/p&gt;

&lt;p&gt;Let's take databases as an example. &lt;a href="https://aws.amazon.com/dynamodb/"&gt;DynamoDB&lt;/a&gt; is a popular, fast and flexible NoSQL database service provided by AWS. It's actually my second favourite AWS service (just beaten by &lt;a href="https://aws.amazon.com/step-functions/"&gt;Step Functions&lt;/a&gt; if you're interested). DynamoDB is largely schemaless. The schema of the items you write is determined at runtime.&lt;/p&gt;

&lt;p&gt;What this means is that you can store almost anything in DynamoDB, in any format. It's an incredibly flexible database that could probably fit almost any application use case.&lt;/p&gt;

&lt;p&gt;That flexibility is one of DynamoDB's biggest strengths, and also it's greatest downfall if misused. If you spend all of your time learning about the specific API's of DynamoDB, you understand single-table design, global secondary indexes, sparse indexes, read and write capacity units but never understand the why... Well then there is every possibility you may choose to use DynamoDB in a scenario where it doesn't actually fit.&lt;/p&gt;

&lt;p&gt;Traditional relational databases still have a perfectly valid use case in modern technology. As do graph and time series databases. There is a reason AWS (currently) has &lt;a href="https://aws.amazon.com/products/databases/"&gt;15 different database services&lt;/a&gt;. Yet if you are stuck in your little DynamoDB bubble you never even look up to see what else is out there.&lt;/p&gt;

&lt;p&gt;I'm not suggesting everyone needs to go deep on every single different type of database, but understanding the history of why we got to where we are is important. &lt;a href="https://www.amazon.co.uk/Designing-Data-Intensive-Applications-Reliable-Maintainable/dp/1449373321"&gt;Designing Data-Intensive Applications by Martin Klepmann&lt;/a&gt; is an absolute goldmine for this content. Even reading the first 3 chapters of what is an admittedly dense technical book, would give you a &lt;strong&gt;huge&lt;/strong&gt; architectural advantage because you understand enough to make an informed decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Generalist Growth Mindset
&lt;/h2&gt;

&lt;p&gt;In the excellent book &lt;a href="https://www.amazon.co.uk/Range-Key-Success-Performance-Education/dp/1509843523"&gt;Range&lt;/a&gt; David Epstein discusses how being a generalist is a fantastic skill to have. The cross-pollination of different ideas, from different domains, leads to better decision making and more creative thinking.&lt;/p&gt;

&lt;p&gt;Having a general idea of the use cases for the different database options, a set of first principles that guide your decision making, forces you to start from the problem you are solving and work outwards to &lt;strong&gt;what&lt;/strong&gt; database you are going to use. Suddenly, the decision between 16 different AWS database services becomes less challenging. You're making your decision based on the specific problem domain, applying your general knowledge of all databases to work out to the best technology choice.&lt;/p&gt;

&lt;p&gt;Once you've gone through the why and reached the what, it's time to double down on a growth mindset. &lt;a href="https://www.ted.com/speakers/carol_dweck"&gt;Professor Carol Dweck&lt;/a&gt; proposed the idea of fixed and growth mindsets. A person holding a fixed mindset assumes that their abilities are immutable, whereas a person with a growth mindset acknowledges that they can acquire and develop any new skill with sufficient time and effort.&lt;/p&gt;

&lt;p&gt;Fixed = I don't know&lt;br&gt;
Growth = I don't know, &lt;strong&gt;yet&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Baseline your thinking as a generalist and start from first principles, before doubling down on your growth mindset to pickup a new technology as and when the time arises.&lt;/p&gt;

&lt;p&gt;Starting from why; a skill all architects, aspiring or otherwise, should cultivate at all times.&lt;/p&gt;

&lt;p&gt;As always, thanks for reading.&lt;br&gt;
James&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>design</category>
      <category>designpatterns</category>
      <category>aws</category>
    </item>
    <item>
      <title>Essentialism in Software</title>
      <dc:creator>James Eastham</dc:creator>
      <pubDate>Fri, 31 Mar 2023 06:41:40 +0000</pubDate>
      <link>https://dev.to/jeastham1993/essentialism-in-software-2hd6</link>
      <guid>https://dev.to/jeastham1993/essentialism-in-software-2hd6</guid>
      <description>&lt;p&gt;&lt;em&gt;"The Way of the Essentialist involves doing less, but better, so you can make the highest possible contribution. The Way of the Essentialist isn’t about getting more done in less time. It’s not about getting less done. It’s about getting only the right things done."&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gregmckeown.com/books/essentialism/"&gt;Greg McKeown&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://gregmckeown.com/books/essentialism/"&gt;Essentialism&lt;/a&gt; is a book that I frequently revisit, typically at least once or twice per year. The core idea is to focus all of your energy on one thing and do it well, rather than spreading yourself thin across multiple tasks. This approach differs from the norm, where many of us tend to work on multiple things simultaneously, leading to a dissipation of our energy in different directions.&lt;/p&gt;

&lt;p&gt;This might be multi-tasking in the well known sense. Focused work, whilst checking email every couple of minutes and being distracted by constant Slack notifications. Or it might be the multi-tasking that sits slightly under the surface. The kind that has us working on multiple different projects, in multiple different disciplines at the same time.&lt;/p&gt;

&lt;p&gt;Having interests in multiple different areas is a great strategy, it allows us to cross-polinate ideas and solve problems in innovative ways. This is discussed at length in the excellent book &lt;a href="https://www.amazon.com/Range-Generalists-Triumph-Specialized-World/dp/0735214484"&gt;Range&lt;/a&gt; by David Epstein. But a clear seperation helps our brains focus.&lt;/p&gt;

&lt;p&gt;I've been particularly guilty of this myself. In the past, I would sign up for any and everything that even remotely related to my interests. Learning to say no or delegate has been a tough lesson that I've had to learn in the last 18 months. While it may work for a little while, and you may achieve some success, it's not a sustainable strategy. Saying yes to everything and attempting to advance multiple projects simultaneously inevitably results in little progress anywhere.&lt;/p&gt;

&lt;p&gt;Have one focus, commit fully, then move on to the next thing. The next idea. The next area of interest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Essentialism in software
&lt;/h2&gt;

&lt;p&gt;You're probably wondering how this relates to software. But there are some interesting ideas we can take from essentialism and apply it to the way we think about software.&lt;/p&gt;

&lt;p&gt;This isn't a new idea. In Domain Driven Design, Eric Evans discusses core, generic, and supporting subdomains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Core: The unique selling point (USP) of your business, the thing that sets you out from your competitors and delights your custoemrs. &lt;/li&gt;
&lt;li&gt;Supporting: Areas of the system that are required for your core domain, but aren't differentiating with the competition and there aren't any off the shelf solutions available. &lt;/li&gt;
&lt;li&gt;Generic: Areas of the system that are completely undifferentiating.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Already, you can start to see how essentialism fits. As much as possible, the focus of a software organisation should be in the core domain. Improvement in this area, will have the most impact on your customers.&lt;/p&gt;

&lt;p&gt;Yet, even with this in mind I see many companies getting lost in supporting and generic subdomains forgetting the value they actually want to deliver to customers. They are expending energy in every possible direction, instead of a single minded focus.&lt;/p&gt;

&lt;h2&gt;
  
  
  Undifferentiated Heavy Lifting
&lt;/h2&gt;

&lt;p&gt;Werner Vogels, CTO of Amazon, is well known for talking about undifferentiated heavy lifting. The cloud enables you to shovel this work on to the cloud provider leaving you to focus on adding value. This is a big step towards essentilism. Gone are the days of racking servers and crossing your fingers that the backup generator kicks in quickly during a power cut. &lt;/p&gt;

&lt;p&gt;But even in the modern, &lt;em&gt;'cloud native'&lt;/em&gt; world, there still seems to be an obsession with continuing to focus on this lifting. On these supporting and generic subdomains that your customers don't care about. Yes, your customers care that your application is always accessible. They don't care if you're doing that with serverless compute or with a container orchestrator.&lt;/p&gt;

&lt;p&gt;Let's stick with the container orchestrator idea for a moment. I don't want this next section to feel like I'm shitting on Kubernetes and I fully appreciate there are valid use cases for a container orchestrator. But let's compare and contrast with something like &lt;a href="https://aws.amazon.com/lambda/"&gt;AWS Lambda&lt;/a&gt;, or if containers float your boat then &lt;a href="https://aws.amazon.com/apprunner/"&gt;AWS AppRunner&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's consider this for a startup or early stage organisation trying to launch a new idea. &lt;/p&gt;

&lt;p&gt;Even when levearging managed services like &lt;a href="https://aws.amazon.com/eks/"&gt;Amazon Elastic Kubernetes Service (EKS)&lt;/a&gt;, there is an awful lot of infrastructure management and configuration needed. This is before you even get the first MVP of your product in front of a customer.&lt;/p&gt;

&lt;p&gt;Comparing that with a service like AWS Lambda, where I can write a piece of code that focuses &lt;strong&gt;only&lt;/strong&gt; on my business logic, deploy that to the cloud and be confident it will scale when required. No infrastructure management, no setup beforehand. An unparalleled time to value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Low Code Applications
&lt;/h2&gt;

&lt;p&gt;There is a step even a beyond Lambda and serverless compute. A step that enables applications to be built without a single line of application code. What application code requires the least amount of heavy lifting? Code that is never written.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/step-functions/"&gt;AWS Step Functions&lt;/a&gt; is my favorite AWS service. It offers direct integration with &lt;strong&gt;almost any&lt;/strong&gt; AWS service through its SDK integrations. By leveraging this capability, you can potentially build out large parts of your supporting and generic subdomains without writing a single line of application code. With no need for infrastructure management, configuration, or even updates to libraries and changes to code, your non-core subdomains become about as low lift as they could possibly be.&lt;/p&gt;

&lt;p&gt;If you're interested, I have a &lt;a href="https://github.com/jeastham1993/event-driven-serverless-cdk"&gt;repository on GitHub&lt;/a&gt; that implements a review translation and sentiment analysis service without a single line of application code. Or if video is your thing, my talk at the &lt;a href="https://www.youtube.com/watch?v=vNrFmsgVWNM"&gt;AWS Community Summit 2022&lt;/a&gt; where I discuss this repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Essentilist Spectrum
&lt;/h2&gt;

&lt;p&gt;I'm not a purist, and appreciate there are absoloutely scenarios where greater control is required. GPU heavy workloads, developer familiarity, longer running workloads. This isn't a debate about Kubernetes vs Lambda vs native service integrations. That was meant purely as an example.&lt;/p&gt;

&lt;p&gt;However, all of these ideas sit along a spectrum of essentialism. At each stage, you choose to introduce an additional pressure on the energy your organisation has to move forwards. To implement features. To delight your customers.&lt;/p&gt;

&lt;p&gt;This spectrum fits nicely with a &lt;em&gt;'serverless first'&lt;/em&gt; mindset. My view on serverless first is quite simple. Default to as little undifferentiated heavy lifting as possible and increase as required. I would typically couple this with a view on core vs non-core subdomain.&lt;/p&gt;

&lt;p&gt;For example, if I am designing a new feature for a service that exists as a supporting sub domain I would go through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step Functions with native service integrations&lt;/li&gt;
&lt;li&gt;Lambda&lt;/li&gt;
&lt;li&gt;AppRunner&lt;/li&gt;
&lt;li&gt;Fargte on ECS&lt;/li&gt;
&lt;li&gt;EC2 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can apply similar heuristics to areas outside of compute (DynamoDB vs RDS for example).&lt;/p&gt;

&lt;p&gt;I realise many decisions are a lot more nuanced, and jumping straight into the implementation details is not typically where I would begin. But once we are down in the weeds, I find this an excellent way to add business value whilst reducing operational overhead.&lt;/p&gt;

&lt;p&gt;Our software is essentialist.&lt;/p&gt;

&lt;p&gt;Thanks for reading,&lt;br&gt;
James&lt;/p&gt;

</description>
      <category>architecture</category>
    </item>
    <item>
      <title>Thinking in Patterns; The Importance of Language in Software Architecture</title>
      <dc:creator>James Eastham</dc:creator>
      <pubDate>Sun, 26 Mar 2023 20:16:20 +0000</pubDate>
      <link>https://dev.to/jeastham1993/thinking-in-patterns-the-importance-of-language-in-software-architecture-2jom</link>
      <guid>https://dev.to/jeastham1993/thinking-in-patterns-the-importance-of-language-in-software-architecture-2jom</guid>
      <description>&lt;p&gt;Language is the foundation upon which all communication is built. It's how we share our thoughts, feelings and ideas with one another. And in the world of software, language is just as critical. How we talk about and describe the structures and patterns we use to build software can have a significant impact on the success of our projects. &lt;/p&gt;

&lt;p&gt;This becomes even more important when building serverless, event driven applications built up of small, loosely joined pieces of functionality. It's like playing with LEGO bricks, but instead of creating a colorful spaceship, you're constructing a well-oiled software machine. And we've all been there when we can't quite understand how two pieces fit together. In this blog post, we'll explore the importance of language in software architecture and how the words we use can shape the way we think.&lt;/p&gt;

&lt;h2&gt;
  
  
  Patterns Aren't a New Thing
&lt;/h2&gt;

&lt;p&gt;Software patterns aren’t a new concept. The famous &lt;a href="https://www.amazon.co.uk/Design-Patterns-Object-Oriented-Addison-Wesley-Professional-ebook/dp/B000SEIBB8"&gt;‘gang of four’  published Design Patterns&lt;/a&gt;  back in October 1994. Whilst focused on object-oriented programming languages like C# and Java, it provided terminology to help developers communicate with each other.&lt;/p&gt;

&lt;p&gt;If someone mentions the builder pattern to me, I understand what they are trying to achieve.&lt;/p&gt;

&lt;p&gt;It also aids the readability of code. If I jump into a new code base and see classes named &lt;code&gt;OrderFactory&lt;/code&gt; or &lt;code&gt;ConfigurationBuilder&lt;/code&gt; I have a rough idea of what the intended functionality of that object is without needing to work through multiple code files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Patterns reduce cognitive load, providing helpful guidance to our brains when processing new information&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There is a part of our brain (the neocortex for any fellow neuroscience nerds) that recognises patterns. The pattern recognition process involves matching information new information with information already stored in the brain. Using a common language aids that matching process, much like a memory pointer in programming.&lt;/p&gt;

&lt;p&gt;Following on from the Gang of Four book, &lt;a href="https://www.amazon.co.uk/Patterns-Enterprise-Application-Architecture-Addison-Wesley-ebook/dp/B008OHVDFM/ref=sr_1_1"&gt;Patterns of Enterprise Application Architecture&lt;/a&gt; and &lt;a href="https://www.amazon.co.uk/Enterprise-Integration-Patterns-Designing-Addison-Wesley-ebook/dp/B007MQLL4E/ref=sr_1_1"&gt;Enterprise Integration Patterns&lt;/a&gt; were both published in 2012. Both are brilliant books to have as a reference if you don’t have them sat on your bookshelf already.&lt;/p&gt;

&lt;p&gt;These books are a reference guide to help us talk about the systems we build. To give us a common language, a set of patterns that describe the systems we are building. Somehow, this got lost along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lost in a sea of implementation details
&lt;/h2&gt;

&lt;p&gt;When was the last time you saw an architecture diagram or design that mentioned a channel adapter? Or using scatter-gather to meet our business use case. Instead, as architects, we default (and I’m guilty of this as much as anyone) to the implementation details. To the services sat underneath. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8kmzy7d0mtg3js2l3buw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8kmzy7d0mtg3js2l3buw.png" alt="Image description" width="800" height="835"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I pulled this diagram straight from a repository on my GitHub. For someone unfamiliar with AWS services, how would you understand what is going on here? You could establish there are 5 separate components, each made up of 2 or more individual pieces. Yes, you could deep dive into the code and try to work it out. But this diagram tells me nothing about my architecture, and everything about my implementation details.&lt;/p&gt;

&lt;p&gt;Both are important, of course. But we are missing a step. We are jumping straight to the implementation without considering what patterns we want to use.I have a particular liking for Amazon Event Bridge. If I need to communicate between 2 systems asynchronously, that’s my default. This is a bias I’m trying to break. So let’s explore this requirement in a little more detail.&lt;/p&gt;

&lt;p&gt;There are 2 systems that need to communicate asynchronously, which might seem like a pretty straightforward ask. Depending on biases you hold from systems you’ve worked on in the past, or what you're most familiar with, you’ll probably end up with one of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon EventBridge&lt;/li&gt;
&lt;li&gt;Amazon SNS&lt;/li&gt;
&lt;li&gt;Amazon SQS&lt;/li&gt;
&lt;li&gt;Amazon Kinesis&lt;/li&gt;
&lt;li&gt;Kafka&lt;/li&gt;
&lt;li&gt;RabbitMQ&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Other message broker/streaming/queuing system that allows components to be decoupled&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The question then becomes, which one do we pick? Comparing service to service at an implementation detail level will lead to a lot of confusion. It’s the most common question I get asked when talking about event driven architectures. What’s the difference between SNS &amp;amp; EventBridge? It’s confusing, right?&lt;/p&gt;

&lt;p&gt;There’s an easier way than getting lost in the features of a specific service, though. That’s through good communication and the use of patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Better Way
&lt;/h2&gt;

&lt;p&gt;This is where communication is a vital tool in the toolbox of any architect. The answer to the question of ‘I need to communicate between 2 systems asynchronously, which service do I use?’ is, of course, another set of questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do you need to guarantee that only one receiver processes the message?&lt;/li&gt;
&lt;li&gt;Do you need to distribute the message to an unknown number of receivers?&lt;/li&gt;
&lt;li&gt;Do you want to keep each type of message on it's own individual channel?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are a bunch more questions we could ask here. But what began as a simple question that my bias told it could answer with Event Bridge turned out to not quite be that simple. And, in my hypothetical scenario, the business requirement is for strict ordering and a guarantee that only one receiver ever processes the message. My first decision was one that future James would not have thanked me for.&lt;/p&gt;

&lt;p&gt;Taking that same list of questions, we can apply a pattern which then leads to the right implementation detail falling out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do you need to guarantee that only one receiver processes the message?

&lt;ul&gt;
&lt;li&gt;Point to point channel / Amazon SQS&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Do you need to distribute the message to an unknown number of receivers?

&lt;ul&gt;
&lt;li&gt;Publish Subscribe / Amazon Event Bridge&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Do you want to keep each type of message on it's own individual channel?

&lt;ul&gt;
&lt;li&gt;Message Channels / Amazon SNS&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Describing our architectures through the lens of patterns &amp;amp; requirements, as opposed to services, simplifies the communication between both existing and new developers. If I join a new team, and I’m told we have a point-to-point integration between these two services that guarantees only one receiver processes the message, I have learned an awful lot about the system function. If I’m shown an architecture diagram that contains a map of service icons, I’m up the creek of implementation details without a paddle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Knock on Affects
&lt;/h2&gt;

&lt;p&gt;There’s a more subtle benefit to thinking about systems in this way, and that’s one of bias. I might work for weeks on a well-architected system using specific AWS services only to be told that ‘You can’t use Event Bridge because the security team said so’. My architecture diagram falls to pieces. I might push back, or spend hours with the security team discussing what we can do to make Event Bridge available. This might be the right approach, but there’s also a pretty good chance it’s &lt;a href="https://thedecisionlab.com/biases/the-sunk-cost-fallacy"&gt;sunk cost bias at play&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If I built my architecture diagram using patterns, instead of an Event Bridge service icon, there would be a ‘publish subscribe’ channel. Event Bridge isn’t usable, ok then let’s use SNS. The CTO mandates we must use non-AWS managed services, ok let’s substitute in Kafka or RabbitMQ.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsb2pa969887tud9krgtp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsb2pa969887tud9krgtp.png" alt="Image description" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One might look prettier, but the second much more clearly demonstrates my intentions as an architect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Think in Patterns, not in services
&lt;/h2&gt;

&lt;p&gt;The risk here, is a re-introduction of the ivory tower architect. An architect who thinks in patterns and abstract concepts but never spends any time building. That is an important point to close this post. Think in patterns, design in patterns, but never lose that builder mindset. The balance is key in the world of software architecture.&lt;/p&gt;

&lt;p&gt;Thanks for reading,&lt;br&gt;
James&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>serverless</category>
      <category>aws</category>
    </item>
    <item>
      <title>Event Modeling By Example</title>
      <dc:creator>James Eastham</dc:creator>
      <pubDate>Wed, 21 Jul 2021 20:19:13 +0000</pubDate>
      <link>https://dev.to/jeastham1993/event-modeling-by-example-396g</link>
      <guid>https://dev.to/jeastham1993/event-modeling-by-example-396g</guid>
      <description>&lt;p&gt;Good design is a fundamental thing for me when it comes to building applications. Yes, there is a time in a place for a quick functional program that does a simple job ala scheduled batch file. But in almost all cases, committing even a small amount of time to thoughtful design has huge ramifications on the long-term success of a project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design for where you want your application to be, not where it is now&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An element of this is the principles of Domain-Driven Design (DDD). For those who aren't aware, DDD is the name of a book written by Eric Evans in 2003 detailing a methodology for designing software. At its core, it maps business domain concepts into software artifacts.&lt;/p&gt;

&lt;p&gt;Whilst I can heartily recommend the &lt;em&gt;big blue book&lt;/em&gt;, I did find it difficult to take practical steps that I can use when working with clients in the real world. That is where event modeling comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to event modeling
&lt;/h2&gt;

&lt;p&gt;Event Modeling is a term coined by &lt;a href="https://eventmodeling.org" rel="noopener noreferrer"&gt;Adam Dymitruk&lt;/a&gt;. It builds upon a lot of the concepts set out in DDD. The main focus is on the events that happen in a business, as all software is at the core is a series of events happening one after another.&lt;/p&gt;

&lt;p&gt;If the software focuses on things that the business can understand, the system can be easily understood by anybody looking at it. That includes everyone from C suite execs through the development team and even to customer service representatives with no background in IT.&lt;/p&gt;

&lt;p&gt;There are some fantastic resources on Adam's website &lt;a href="https://eventmodeling.org" rel="noopener noreferrer"&gt;https://eventmodeling.org&lt;/a&gt;, but what I wanted to-do today was walk through my process for implementing event modeling in a 'real' application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step by Step
&lt;/h2&gt;

&lt;p&gt;Before I dive into the step-by-step, I want to identify a couple of tools and resources I use for this process.&lt;/p&gt;

&lt;p&gt;The first is the article on Adam's website on &lt;a href="https://eventmodeling.org/posts/what-is-event-modeling/" rel="noopener noreferrer"&gt;'What is event modeling'&lt;/a&gt;. It adds a lot of context to the steps I'm going to follow in this article. I can recommend going to read that article before continuing with this one. &lt;/p&gt;

&lt;p&gt;The second is &lt;a href="https://miro.com" rel="noopener noreferrer"&gt;Miro&lt;/a&gt;. Miro is a virtual whiteboard that allows multiple people to collaborate on the same board. Given more and more of the world is moving to a completely remote way of working, it's an invaluable tool when it is simply impossible to all stand at a physical whiteboard.&lt;/p&gt;

&lt;p&gt;Even if you do use a physical whiteboard initially, it's a great idea to move that to a digital tool for future developments. Online, backed up, and accessible anywhere - what's not to love.&lt;/p&gt;

&lt;p&gt;So now, on to the steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Brainstorming
&lt;/h3&gt;

&lt;p&gt;The first step of the process is to brainstorm ALL of the events that affect within the business domain being modeled. It's important to have stakeholders from &lt;strong&gt;all&lt;/strong&gt; parts of the business in this session as each may have a valuable piece of insight into the workflows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Fbrain-storm.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Fbrain-storm.PNG" alt="virtual post-it notes showing events"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What you are looking for here is just a brain dump of all the things happening in the business. There is no need to organize or categorize at this stage, just get it all out there. Remember, an event is always in the past tense. SubmitOrder is not an event but OrderSubmitted is.&lt;/p&gt;

&lt;h3&gt;
  
  
  Timeline
&lt;/h3&gt;

&lt;p&gt;The second step is the first layer of organisation. All of the events should be added to a timeline that shows the journey through the system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Ftimeline.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Ftimeline.PNG" alt="virtual timeline"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For anyone who has ever ordered food online, this series of events should be familiar.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;OrderSubmitted&lt;/li&gt;
&lt;li&gt;StockChecked&lt;/li&gt;
&lt;li&gt;PaymentProcessed&lt;/li&gt;
&lt;li&gt;OrderCooked&lt;/li&gt;
&lt;li&gt;OrderDispatched&lt;/li&gt;
&lt;li&gt;OrderDelivered&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The other thing you'll notice on this diagram is that I've identified a couple of important events. These events are the ones that are fundamental to the business. In this example, taking payments and delivering orders are the two things needed for the restaurant to succeed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Storyline
&lt;/h3&gt;

&lt;p&gt;The third phase is to begin thinking about user interactions and the different people taking part in the process. Wireframe mockups are a great tool.  These mock-ups don't need to be concrete though they are just to give a rough idea of the interactions with the system. &lt;/p&gt;

&lt;p&gt;I find it helpful to split these down into swimlanes for the different people/systems who affect the system. It would also include any external systems (payment processors, CRM systems) that change state, albeit without direct human intervention.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Fstory-line.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Fstory-line.PNG" alt="storyline of event flow with wire frames"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example, I'm more concerned about the backend application than the look of the frontend. For that reason, I've just included some icons to indicate there will be interactions from either a UI or an external system.&lt;/p&gt;

&lt;p&gt;Notice how I've included the cog icons for the stock checker and payment processor. It shows that there is a process that happens to cause a change in the system state. The interaction may not be driven by a human, but it is an interaction all the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interactions
&lt;/h3&gt;

&lt;p&gt;Phase four is where things start to get interesting. It is where I begin to link the UI/UX to the events via commands.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Finteractions.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Finteractions.PNG" alt="storyline of event flow commands included"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Taking the first workflow as a specific example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A customer interacts with a UX screen that allows them to make an order&lt;/li&gt;
&lt;li&gt;The UX sends a SubmitNewOrder command&lt;/li&gt;
&lt;li&gt;A OrderSubmitted event is raised&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Taking a look at the whiteboard now, there is a clear flow from left to right. This interaction submits this command then this event happens.&lt;/p&gt;

&lt;p&gt;It can be beneficial at this point to talk about the command contents. Thinking about what makes up a SubmitStockCheckResults command can help to identify any missing data from earlier in the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Informing the users
&lt;/h3&gt;

&lt;p&gt;Phase five is where I start to look at the information users need to make decisions.&lt;/p&gt;

&lt;p&gt;I do this using bespoke read models that project the event information into a useful format for users. You can see these below on the green post-it notes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Fviews.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Fviews.PNG" alt="storyline of event flow read model included"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the stock checker to reliably check the stock of orders, it needs a view of all the orders that are currently waiting to have their stock checked. For the kitchen to cook the orders, they need to see a list of all the orders that have been paid and are awaiting production.&lt;/p&gt;

&lt;p&gt;Again, it's important to not get bogged down in details here. It doesn't matter if you're using a queue, an event stream, or a relational database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What matters is that the data needs to be exposed in a friendly format that can be used to make informed decisions about the system.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Adam refers to these read models as to-do lists, and I quite like that idea. A to-do list is a concept that almost everybody can understand. If I were to start talking about queues or a messages bus I would likely alienate the non-technical folk. &lt;/p&gt;

&lt;p&gt;The system is saying here are some data items that need to have an action performed on them.&lt;/p&gt;

&lt;p&gt;I find it's at this stage that any holes start to become apparent. For example, in the above screenshot, a SubmitPaymentResult command happens results in a PaymentProcessed event. This event has a PaymentSuccess field that looks like it should be conditional in the workflow.&lt;/p&gt;

&lt;p&gt;When a PaymentProcessed event happens the order is placed on the OrdersAwaitingCook to-do list. But what if the payment fails?&lt;/p&gt;

&lt;p&gt;And this is where the value of event modeling becomes apparent. You have got a room full of people, both technical and not, who can talk the same language and rationalize about the behavior of the system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Architect: "So, what happens when the payment is processed but it fails for some reason?"

Mark in Order Processing: "Oh, if that happens I would just notify the customer to inform them that their payment has failed."

Architect: "Ok, so how would that interaction happen?"

Barbara in Customer Service: "Well, if the customer was stood in-front of me in the restaurant I could ask them to change their payment method. If they are ordering online, then I need them to know about the failure instantly so they can change their order, or at least know that they aren't going to be getting a pizza delivered."

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a matter of moments, I have learned something about our domain and can amend the flow of events on the fly. In the first iteration of the timeline, I processed payments and then added all orders to the OrdersAwaitingCook to-do list. Now, I have a small branch at this point in the case of an order failing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Fviews-v2.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Fviews-v2.PNG" alt="refactored storyline with views including payment failure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Service grouping
&lt;/h3&gt;

&lt;p&gt;Almost there now.&lt;/p&gt;

&lt;p&gt;The penultimate stage is to group the events into their service layers. Hopefully by this stage, you should start to see some pretty logical boundaries between the different events in your system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Fservice-layers.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Fservice-layers.PNG" alt="event flow with service groupings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An important note here, these service layers should be based on the business domain and &lt;strong&gt;NOT&lt;/strong&gt; on a perceived microservices architecture.  Remember, you are trying to keep everybody interested and on the same page.&lt;/p&gt;

&lt;p&gt;It can be good at this stage to think about the make-up of teams. A team of people, technical and non-technical, can now begin work on an individual service layer. These service layers can then develop independently, but the shared vision is also well understood.&lt;/p&gt;

&lt;p&gt;Yep, it sounds a lot like microservices. Resist the temptation, don't say the word!&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenarios
&lt;/h3&gt;

&lt;p&gt;The final stage is to build specific scenarios out of the workflows. A scenario is a user story that is built from this set of events, commands, and views.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Fscenarios.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Fscenarios.PNG" alt="a view of different system scenarios / workflows"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now what I have is a list of very specific functional requirements that when pieced together form the basis of the entire system. Each of these individual workflows can be assigned to a specific team and that is the workflow they own.&lt;/p&gt;

&lt;p&gt;From here, a more developer-specific specification may be created in the form of a more traditional user story. What is important however, is that this event model is treated as the source of truth that the &lt;strong&gt;ENTIRE ORGANISATION&lt;/strong&gt; can understand.&lt;/p&gt;

&lt;p&gt;The development teams may have epics in Jira or cards on a Trello board. It's largely irrelevant. The importance is the common language the entire system shares and can be used to rationalize different scenarios.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Notes
&lt;/h2&gt;

&lt;p&gt;There are a couple of additional bits of information that I just wanted to tack on at the end to answer some questions I know I had when I first encountered this model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation is irrelevant
&lt;/h3&gt;

&lt;p&gt;If there is one I would take away from this, it is that the implementation is irrelevant. Whether that's database providers, to-list implementations, programming languages. Don't worry about any of it.&lt;/p&gt;

&lt;p&gt;The end goal should be for somebody non-technical and unfamiliar with the business to be able to understand what happens within the organisation very quickly.&lt;/p&gt;

&lt;p&gt;Imagine starting work at a new company. In your first day, you understand the entire system at a high level and begin to ask questions of both domain experts and developers in a language everybody understands. It wouldn't matter if you were working in a technical or non-technical role.!&lt;/p&gt;

&lt;h3&gt;
  
  
  SLA's can be important
&lt;/h3&gt;

&lt;p&gt;Whilst the implementation is irrelevant it can be useful to note on the diagram any time-sensitive operations. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Fsla.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Fevent-modeling-by-example%2Fsla.PNG" alt="a view of different system scenarios / workflows"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see I've marked very explicitly there is a business-specific rule that a stock check must complete in 10 seconds. This is to provide feedback to a user in real-time when they are using the application.&lt;/p&gt;

&lt;p&gt;Remember the detail of 'real-time' information isn't important (SignalR, websockets, emails, etc), it is just that there is some form of service level agreement that the system needs to adhere to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wireframing
&lt;/h3&gt;

&lt;p&gt;The wireframes on all of my screenshots are icons taken from Miro's built-in icon sets. My intention with this document is to show the backend architecture of a system using event modeling. I didn't want to get too bogged down in front-end appearance. At least at this stage.&lt;/p&gt;

&lt;p&gt;In a normal working session, these wireframes would be drawn up to look like how you'd actually want the UI to look. If the UI was going to have three inputs, a dropdown, and a button go ahead and create a wireframe in Miro that looks like that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;I hope you've found this step-by-step useful and that it has given you a good introduction to the concept of event modeling.&lt;/p&gt;

&lt;p&gt;I know it's something I'll be using in future architecture discussions as another tool to bridge the gap between business domain and technical details.&lt;/p&gt;

&lt;p&gt;A huge thanks to Adam Dymitruk for sharing so much information about this process. There is an abundance of YouTube videos out there containing real-world scenarios that Adam works through with businesses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://miro.com/app/board/o9J_l5lypvI=/" rel="noopener noreferrer"&gt;Miro Board used in this example&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://eventmodeling.org/posts/what-is-event-modeling/" rel="noopener noreferrer"&gt;https://eventmodeling.org/posts/what-is-event-modeling/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://eventmodeling.org/posts/event-modeling-cheatsheet/" rel="noopener noreferrer"&gt;https://eventmodeling.org/posts/event-modeling-cheatsheet/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=SqaDYwgQ664&amp;amp;feature=youtu.be" rel="noopener noreferrer"&gt;Event Modeling with Adam Dymitruk at the DDD Greece Meetup&lt;/a&gt;&lt;/p&gt;

</description>
      <category>eventdriven</category>
      <category>architecture</category>
      <category>systems</category>
      <category>domaindrivendesign</category>
    </item>
    <item>
      <title>A redefinition of serverless applications</title>
      <dc:creator>James Eastham</dc:creator>
      <pubDate>Mon, 12 Jul 2021 19:15:35 +0000</pubDate>
      <link>https://dev.to/jeastham1993/a-redefinition-of-serverless-applications-1a1g</link>
      <guid>https://dev.to/jeastham1993/a-redefinition-of-serverless-applications-1a1g</guid>
      <description>&lt;p&gt;&lt;em&gt;"Serverless is a way to describe the services, practices, and strategies that enable you to build more agile applications so you can innovate and respond to change faster. With serverless computing, infrastructure management tasks like capacity provisioning and patching are handled by AWS, so you can focus on only writing code that serves your customers."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;em&gt;Definition of serverless, taken from &lt;a href="https://aws.amazon.com/serverless/" rel="noopener noreferrer"&gt;AWS&lt;/a&gt;&lt;/em&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For a long time, I had quite a close-minded view of Serverless. I made heavy use of AWS Lambda and DynamoDB and stitched it all together using an API Gateway. Development productivity was through the roof, life was great.&lt;/p&gt;

&lt;p&gt;Until it wasn't.&lt;/p&gt;

&lt;p&gt;I was working on a new project which had an extremely variable load (it could go for weeks without being used) but still needed really low latency when it was accessed.&lt;/p&gt;

&lt;p&gt;Lambda is great and with provisioned concurrency enabled it was ok from a latency perspective, but the business wanted more. I was out of ideas.&lt;/p&gt;

&lt;p&gt;My close-minded view of Serverless kept me banging my head against a wall of cold starts and application tracing to try and eek out as much performance as possible. It was a frustrating time. I knew Lambda was fantastic but couldn't quite meet the requirements of the customer.my def&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2FServerless%2520Application%2520Architecture.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2FServerless%2520Application%2520Architecture.png" alt="First version of Serverless Architecture Diagram"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Initial simplified architecture&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  A change in approach
&lt;/h2&gt;

&lt;p&gt;That changed when I listed to an AWS podcast episode, specifically &lt;a href="https://aws.amazon.com/podcasts/448-aws-copilot-modern-applications-cli/" rel="noopener noreferrer"&gt;episode #448&lt;/a&gt; talking about AWS Copilot. It struck me for two reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I've always been a big fan of ECS, but the cluster configuration, networking and general management was always a blocker. I'll openly admit networking and server management is a weak part of my skillset, and I've never learnt. Deploying from a CLI to an AWS best practice environment removes that blocker completely.&lt;/li&gt;
&lt;li&gt;There is more to Serverless than AWS Lambda.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And it's the second point that really struck me, there is more than one way to deploy an application in a serverless environment. I'm looking at you Fargate, and you App Runner.&lt;/p&gt;

&lt;p&gt;Taking a line of text directly from the Copilot documentation - &lt;em&gt;Request-Driven Web Service An AWS App Runner service that auto scales your instances based on incoming traffic and scales down to a baseline instance when there's no traffic. This option is more cost-effective for HTTP services with sudden bursts in request volumes or low request volumes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sudden bursts in request volumes or low request volumes - Doesn't that just sound like exactly what I am looking for?&lt;/p&gt;

&lt;p&gt;So what does that give me? It provides a way for me to easily deploy an application that is 'always-on' from an easy-to-use CLI, without the need to worry too much about the underlying infrastructure.&lt;/p&gt;

&lt;p&gt;This is when my definition of Serverless changed, and when the opening AWS quote made a lot more sense. Now I'd define Serverless as&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Any managed service that abstracts away operational complexity and allows developers and architects to focus on business value"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;From AWS Lambda to Azure Functions to Heroku. All of these services provide a platform that removes a barrier for developers and allows them to just get their code out into the wild.&lt;/p&gt;

&lt;p&gt;At a higher scale, there may be an argument for more fine-grained control. But for early startups and small companies, I cannot think of a better and more streamlined way of getting your product out there.&lt;/p&gt;
&lt;h2&gt;
  
  
  A change in architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Frevamped-serverless-architecture.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjameseastham.co.uk%2Fstatic%2Fimages%2Frevamped-serverless-architecture.png" alt="Serverless Architecture Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whilst there isn't anything different at first glance, there is one really important distinction.&lt;/p&gt;

&lt;p&gt;That distinction is AWS App Runner replacing the Lambda function that is handling client requests.&lt;/p&gt;

&lt;p&gt;For people unfamilar with &lt;a href="https://aws.amazon.com/apprunner/" rel="noopener noreferrer"&gt;AWS App Runner&lt;/a&gt;, it's a fully managed way of running containers in AWS without the need for managing underlying servers. You simply configure the required amount of CPU and memory and off you go.&lt;/p&gt;

&lt;p&gt;The rest of the architecture is identical. Any asynchronous event-driven tasks are still handled by Lambda. This feels like the use case for which Lambda was originally intended. A set of decoupled event handlers that sit doing nothing (and costing little money) until an event happens they are interested in that they react to accordingly.&lt;/p&gt;

&lt;p&gt;What this means for the system, is that end users get a low latency API response no matter when they access the app. An App Runner task is 'always on' in the same way that running a server would be.&lt;/p&gt;

&lt;p&gt;The trade-off here is an increase in cost, in that App Runner is charged per hour. The cost differs slightly when your application is actually processing requests vs sitting and waiting for a request to arrive. For a REST API, that is 24 hours a day 365 days a year. Slightly different than the Lambda model.&lt;br&gt;
And what it comes down to is the age-old response of 'it depends'. Everything depends on the use case.&lt;/p&gt;

&lt;p&gt;Lambda is a completely viable way to run an entire application, as long as a higher latency on the initial startup is ok. For high-volume API's, this is probably not an issue as the functions will always be warm.&lt;/p&gt;

&lt;p&gt;However, for my specific use case of a low usage low latency application, the cost trade-off is something we are willing to accept to ensure the end-users have the best possible experience.&lt;/p&gt;

&lt;p&gt;The customer always comes first.&lt;/p&gt;

&lt;p&gt;For future applications, especially applications built in an event-driven fashion, this is becoming my de-facto architecture. Fargate / AWS App Runner for the client-facing applications, Lambda for the backend services that are less latency driven and more event-driven.&lt;/p&gt;
&lt;h2&gt;
  
  
  In Practice
&lt;/h2&gt;

&lt;p&gt;Now for a quick example of just how easy it is to get started with AWS Copilot and AppRunner. For that, I'm going to use the order management service in my &lt;a href="https://github.com/jeastham1993/node-restaurant" rel="noopener noreferrer"&gt;node-restaurant&lt;/a&gt; repository.&lt;/p&gt;

&lt;p&gt;Originally, this application was built entirely from lambda functions. The REST API now runs as an express.js application, with any background event handlers running in Lambda. I already had a Dockerfile that I used for some local testing, so I can wire copilot up using that as well. So to get started with copilot, I simply run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
copilot init

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI gives a really great step by step walkthrogh, for reference I selected:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request Driven Web Service&lt;/li&gt;
&lt;li&gt;Name the application (order-api) &lt;em&gt;Important note, AppRunner has a max length on the name field which is the application name plus the service name&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Select the Dockerfile (helpfully, copilot defaults as it's the only one I have)&lt;/li&gt;
&lt;li&gt;Yes to the test environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After a minute or two you should start to see the output from the Docker build command and then the beginning of the deployment. A note here, the first time you push out to AppRunner it does take quite a while to provision the infrastructure. Go grab a coffee or something and come back later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
✔ Proposing infrastructure changes for stack node-restaurant-test-order-api
- Creating the infrastructure for stack node-restaurant-test-order-api            [create complete]  [317.2s]
  - An IAM Role for App Runner to use on your behalf to pull your image from ECR  [create complete]  [17.9s]
  - An Addons CloudFormation Stack for your additional AWS resources              [create complete]  [28.7s]
  - An IAM role to control permissions for the containers in your service         [create complete]  [17.9s]
  - An App Runner service to run and manage your containers                       [create complete]  [285.9s]


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once complete, head off to the App Runner UI, and voila the API is ready to serve requests.&lt;/p&gt;

&lt;p&gt;To come back around to my earlier point, everything always depends! There are benefits and trade-offs to every service, both AWS Lambda and App Runner are suitable for hosting an application. It just depends on the customer use case.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>aws</category>
      <category>architecture</category>
    </item>
    <item>
      <title>The Importance of Resetting to Zero</title>
      <dc:creator>James Eastham</dc:creator>
      <pubDate>Mon, 16 Mar 2020 19:55:51 +0000</pubDate>
      <link>https://dev.to/jeastham1993/the-importance-of-resetting-to-zero-4c7f</link>
      <guid>https://dev.to/jeastham1993/the-importance-of-resetting-to-zero-4c7f</guid>
      <description>&lt;p&gt;I'm sure everybody reading this post has experienced this at some point. You close down your computer on a Friday, excited for the weekend. You then proceed to spend the entire weekend thinking about the week you've just had and pre-empting all of the things that you want to get done the following week.&lt;/p&gt;

&lt;p&gt;Before you know it Monday has come around again and you haven't reaped the benefits of having time away from your desk.&lt;/p&gt;

&lt;p&gt;I spend a lot of my time mulling over these kinds of situations, as I believe work/life balance is one of the most fundamental problems with society today. We are all expected to be always on, and spend our lives never truly disconnecting from the working world.&lt;/p&gt;

&lt;p&gt;Nothing you're about to read in this post is revolutionary, I'm instead regurgitating common tools and tricks I use, without fail, every single week to ensure my time away from the office is well spent.&lt;/p&gt;

&lt;h1&gt;
  
  
  Reset to Zero
&lt;/h1&gt;

&lt;p&gt;I'm sure I'm not the person to coin the phrase reset to zero, but I certainly find it's the most accurate way to describe the system I employ.&lt;/p&gt;

&lt;p&gt;Every week, I ensure that my working life is reset to a state of tranquility. I use these same methods in both my personal and professional life, but I'm confident nobody here wants to read about how I reset my house to zero (FYI, there's house-work involved).&lt;/p&gt;

&lt;h2&gt;
  
  
  What is involved
&lt;/h2&gt;

&lt;p&gt;I use it as a way to make sure that there are no loose ends, no un-sent emails, and no un-committed changes.&lt;/p&gt;

&lt;p&gt;My email inboxes are down to zero, my to-do list is updated and my next week is planned.&lt;/p&gt;

&lt;p&gt;All my time is logged and my clients are aware of my plans for the next week so that I can be sure they will be available if I need to ask questions of them.&lt;/p&gt;

&lt;p&gt;It can take me up to 2 hours to go through everything I need to, which may seem like two hours of wasted time. But the spare mental capacity managing your workload in this way provides, massively offsets the lost time. At least in my book anyway. &lt;/p&gt;

&lt;p&gt;I can honestly say since fully committing to this practice every week my mental stress has dropped massively. No matter what the situation, being able to come back to the feeling of calm and control cannot be understated.&lt;/p&gt;

&lt;p&gt;Especially with the dynamic nature of the industry, we all choose to work in, the power of a clear mind is invaluable.&lt;/p&gt;

&lt;h1&gt;
  
  
  Getting started
&lt;/h1&gt;

&lt;p&gt;If your interest has been piqued, I'm glad to hear it. If not, well why don't you keep reading anyway just to see what you might find out?&lt;/p&gt;

&lt;p&gt;It's a reasonably straight forward process I follow:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Email Inboxes
&lt;/h2&gt;

&lt;p&gt;I view my emails as my community to-do list. The world has access to it, and almost everything that comes in gives me a task to do.&lt;/p&gt;

&lt;p&gt;So my first task is always getting an empty inbox.&lt;/p&gt;

&lt;p&gt;I strictly follow Get Things Done principles when it comes to my email inbox.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anything I need to work on is moved to @action&lt;/li&gt;
&lt;li&gt;Anything I am waiting on somebody else for is moved to @waiting&lt;/li&gt;
&lt;li&gt;Anything else is deleted (with a copy stored in CRM for anything I feel could be useful in the future)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F35gk3prl6k2k0jvzkjry.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F35gk3prl6k2k0jvzkjry.PNG" alt="Alt Text" width="343" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm pretty brutal about the above three rules, especially when it comes to deletion. I've found, after doing this consistently for 18 months, there is almost never a case where it is worth keeping an email cluttering up your mailbox.&lt;/p&gt;

&lt;p&gt;These GTD principles are followed on a day by day basis. When it comes to my weekly review, I process all tasks in the @action, @waiting or Inbox folders out into my second brain.&lt;/p&gt;

&lt;p&gt;Which leads me quite nicely on to&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Project/Development Reviews
&lt;/h2&gt;

&lt;p&gt;A nice short section here, but I run through each and every 'project' I'm working on and check to see what the current state is.&lt;/p&gt;

&lt;p&gt;That would include outstanding commits from my laptop, work still to be done on the project, new feature requests or outstanding issues.&lt;/p&gt;

&lt;p&gt;Basically anything at all that needs my direct input.&lt;/p&gt;

&lt;p&gt;Sometimes this part of the review can take the longest, sometimes it's over and done within a matter of minutes.&lt;/p&gt;

&lt;p&gt;Inevitably there are things that get forgotten here, but knowing that 98% of the things in my head are stored somewhere that isn't my sieve-like brain is a huge plus.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Todo lists (my second brain)
&lt;/h2&gt;

&lt;p&gt;There are a billion and one articles around the internet on the correct way to manage a to-do list. A lot of which are filled with fantastic tips and tricks.&lt;/p&gt;

&lt;p&gt;However, I think the best way to manage your to-do list is the way that works best for you.&lt;/p&gt;

&lt;p&gt;Maybe you prefer one big long list of all the things you need to do. Maybe you prefer big overarching tasks with sub-tasks within them. The practical side of it is largely irrelevant.&lt;/p&gt;

&lt;p&gt;What I always try to achieve with my to-do list, is to remove every single potential thought/thing to remember from my brain and instead ask a piece of technology to remember for me.&lt;/p&gt;

&lt;p&gt;Again, I stick with GTD methods here. Anything that pops into my head during the week, gets added to an Inbox section of my to-do list. Similarly, when I process my emails anything that is important goes into the Inbox.&lt;/p&gt;

&lt;p&gt;Step two of my weekly review is going through this inbox and organizing it into relevant places. This normally entails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New client requests&lt;/li&gt;
&lt;li&gt;Support issues (inc. time-sensitive ones)&lt;/li&gt;
&lt;li&gt;Emails I need to send&lt;/li&gt;
&lt;li&gt;Documents I need to read/review&lt;/li&gt;
&lt;li&gt;Interesting articles to read&lt;/li&gt;
&lt;li&gt;Phone calls to make&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The list goes on.&lt;/p&gt;

&lt;p&gt;These inbox items are organized into folders that follow and assigned times/dates based on the sensitivity of the thing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Professional - Day job&lt;/li&gt;
&lt;li&gt;Professional - Freelance&lt;/li&gt;
&lt;li&gt;Personal - Out and about&lt;/li&gt;
&lt;li&gt;Personal - Home&lt;/li&gt;
&lt;li&gt;Personal - Computer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once done, I almost instantly feel like there is a weight off my shoulders.&lt;/p&gt;

&lt;p&gt;And that's my professional life reset to zero. I apply these same principles to my personal life to ensure my entire world is in as calm a state as possible.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Benefits
&lt;/h1&gt;

&lt;p&gt;I can't rave enough about the benefits of spending the time doing this kind of thing.&lt;/p&gt;

&lt;p&gt;Yes, it's time-consuming. &lt;/p&gt;

&lt;p&gt;Yes, things will still get forgotten and you'll end up with an unexpected client call.&lt;/p&gt;

&lt;p&gt;But once you've spent your first weekend not thinking about your working life because you are absolutely confident everything is in perfect order, you'll be thinking why you didn't start doing it earlier.&lt;/p&gt;

&lt;p&gt;I'm intrigued to hear other people's best practices when it comes to managing your task list. Let me know in the comments...&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>wellbeing</category>
      <category>career</category>
    </item>
    <item>
      <title>Front End for the .NET Developer with Blazor Web Assembly</title>
      <dc:creator>James Eastham</dc:creator>
      <pubDate>Sun, 01 Mar 2020 18:10:08 +0000</pubDate>
      <link>https://dev.to/jeastham1993/getting-started-with-blazor-web-assembly-495a</link>
      <guid>https://dev.to/jeastham1993/getting-started-with-blazor-web-assembly-495a</guid>
      <description>&lt;p&gt;Throughout my entire development career, I've always found web to be the best way of deploying an interface to any software I've written.&lt;/p&gt;

&lt;p&gt;Desktop never really appealed to me, maybe that comes from growing up almost entirely in an age where everything was web-based.&lt;/p&gt;

&lt;p&gt;However, coming from a .NET background, there are some downsides to this web-first approach.&lt;/p&gt;

&lt;p&gt;ASP Web Forms were ok, and MVC built upon that a little bit. But nothing ever really came close to the magic of the big Javascript frameworks.&lt;/p&gt;

&lt;p&gt;That lead me down a rabbit hole I never truly got to grips with, the magical world of Angular and React.&lt;/p&gt;

&lt;p&gt;I've toyed with both, completed tutorials and written production apps. Still, neither of them really clicked fully (I did massively prefer React, but that's a personal preference thing).&lt;/p&gt;

&lt;p&gt;Typescript improved upon this pain slightly, bringing strong typing and a more C#-esque way of development.&lt;/p&gt;

&lt;p&gt;And then, along came Blazor...&lt;/p&gt;

&lt;h1&gt;
  
  
  Blazor
&lt;/h1&gt;

&lt;p&gt;I'm not going to dive into too much detail around Blazor, there is plenty of other articles around the web regarding that (the &lt;a href="https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor"&gt;official docs&lt;/a&gt; are a great place to start).&lt;/p&gt;

&lt;p&gt;To quickly summarise though, Blazor is Microsoft's challenge to the big JS-based front end frameworks. It allows rich and interactive front end apps to be written entirely in C#. Not a line of Javascript insight.&lt;/p&gt;

&lt;p&gt;And with that, let's dive into some code. &lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;To get started with Blazor, I'm going to add a front end to the team-service I've been building throughout this series of articles. It's a learning process for me as well, so stay with me!&lt;/p&gt;

&lt;p&gt;Running the below commands is all it takes to get started with a new Blazor app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new &lt;span class="nt"&gt;-i&lt;/span&gt; Microsoft.AspNetCore.Blazor.Templates::3.2.0-preview1.20073.1 &lt;span class="nt"&gt;-o&lt;/span&gt; LeagueManager
dotnet new blazorwasm &lt;span class="nt"&gt;-o&lt;/span&gt; LeagueManager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first install's the latest Blazor Web Assembly project template (at the time of writing, it's 3.2.0 Preview 1). The second command creates a new project.&lt;/p&gt;

&lt;p&gt;Following the two commands with the below, gets the sample app up and running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; .&lt;span class="se"&gt;\L&lt;/span&gt;eagueManager&lt;span class="se"&gt;\&lt;/span&gt;
dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4sunpxfj3xuck6ixv77v.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4sunpxfj3xuck6ixv77v.PNG" alt="Alt Text" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Out of the box, Blazor applications use Bootstrap and are created with some great examples of the key functionality (namely two-way model binding and making external requests). &lt;/p&gt;

&lt;p&gt;First things first, let's add our own color scheme and font. I'll also remove a lot of the standard Blazor code leaving a much more basic project structure.&lt;/p&gt;

&lt;p&gt;I'm also going to apply one of the principles I learned from React, which is to keep stateful components to an absolute minimum and make the majority of the application dumb.&lt;/p&gt;

&lt;p&gt;For that, I'm going to add two additional folders on top of the pre-configured 'Pages' folder with two new folders:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Components - Dumb, layout components&lt;/li&gt;
&lt;li&gt;Services - Stateful components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Initially, I considered disposing of the Pages folder as well. But keeping actual pages (with routes) in one place seemed more logical.&lt;/p&gt;

&lt;p&gt;For this initial piece of front end of development, I need to be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;View a list of teams&lt;/li&gt;
&lt;li&gt;Create a new team&lt;/li&gt;
&lt;li&gt;View details about a specific team&lt;/li&gt;
&lt;li&gt;Add players to a team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my head, that gives me two pages. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A list of all teams and the ability to create a new team&lt;/li&gt;
&lt;li&gt;A page to view a specific team containing a list of the team's players and corresponding data entry form for adding new players&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Handling State
&lt;/h2&gt;

&lt;p&gt;Before I get into much detail about the page structure, I first wanted to see how HTTP data access works in Blazor.&lt;/p&gt;

&lt;p&gt;Turns out, the answer is extremely simple.&lt;/p&gt;

&lt;p&gt;The answer is a &lt;em&gt;huge +1&lt;/em&gt; already for using Blazor as a front end framework. The code written to manage to retrieve data from an external HTTP service is almost identical to code that would run in any kind of .NET application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Net.Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text.Json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;LeagueManager.Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Logging&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;LeagueManager.Services&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TeamState&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;LastSearch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IReadOnlyList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Team&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TeamSearchResults&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;SearchInProgress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;OnChange&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;HttpClient&lt;/span&gt; &lt;span class="n"&gt;_httpClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TeamState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;TeamState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;HttpClient&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TeamState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_httpClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;DeletePlayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Player&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TeamId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; - &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; - &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Position&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;httpContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StringContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;postResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_httpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DeleteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"http://localhost:8080/team/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TeamId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/players"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSuccessStatusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;NotifyStateChanged&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;postResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsStringAsync&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failure adding player"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;AddPlayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Player&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TeamId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; - &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; - &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Position&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;httpContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StringContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;postResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_httpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PostAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"http://localhost:8080/team/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TeamId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/players"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;httpContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSuccessStatusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;NotifyStateChanged&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;postResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsStringAsync&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failure adding player"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Team&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetSpecific&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;teamId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running HTTP search"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_httpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetJsonAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TeamSearchResponse&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;$"http://localhost:8080/team/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;teamId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Team&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ReRunSearch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LastSearch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;searchTerm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastSearch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;searchTerm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogWarning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running search"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SearchInProgress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="nf"&gt;NotifyStateChanged&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;searchResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_httpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetJsonAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TeamSearchResponse&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;$"http://localhost:8080/team?search=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;searchTerm&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;searchResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TeamSearchResults&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;searchResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Teams&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SearchInProgress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="nf"&gt;NotifyStateChanged&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Failure running search"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;CreateTeam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Team&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;httpContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StringContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;postResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_httpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PostAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"http://localhost:8080/team"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;httpContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSuccessStatusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;NotifyStateChanged&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;postResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsStringAsync&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failure adding team"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;NotifyStateChanged&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OnChange&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A reasonably straightforward class that uses Dependency Injection to add a HttpClient and an ILogger. From there, it is mostly just a set of different methods handling the various different CRUD operations. &lt;/p&gt;

&lt;p&gt;** Note the terrible hardcoded URL, this is a work in progress :) **&lt;/p&gt;

&lt;p&gt;There are a couple of front-end/Blazor specific parts that I do just want to point out, however.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IReadOnlyList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Team&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TeamSearchResults&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;SearchInProgress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;OnChange&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These three properties are all relevant to HTTP in Blazor.&lt;/p&gt;

&lt;p&gt;The Blazor application runs completely in a web browser. Therefore this TeamState object is specific to the client accessing the site.&lt;/p&gt;

&lt;p&gt;Therefore, whenever search runs we can hold a copy of the returned data in a property for easy retrieval if the search needs to happen again.&lt;/p&gt;

&lt;p&gt;SearchInProcess holds a value indicating that the search method is running. Imagine a front end in which a loading bar appears during a search, that could be bound to this property.&lt;/p&gt;

&lt;p&gt;The OnChange property is probably the most important of the three. It's linked directly to this line of code here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;NotifyStateChanged&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OnChange&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, the OnChange property can be subscribed to by an external method. NotifyStateChanged then invokes that external method.&lt;/p&gt;

&lt;p&gt;Let's dive into a real example. Here is the code for the Team List page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;@page&lt;/span&gt; &lt;span class="s"&gt;"/teams"&lt;/span&gt;

&lt;span class="n"&gt;@using&lt;/span&gt; &lt;span class="n"&gt;LeagueManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;
&lt;span class="n"&gt;@using&lt;/span&gt; &lt;span class="n"&gt;LeagueManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Teams&lt;/span&gt;

&lt;span class="n"&gt;@inject&lt;/span&gt; &lt;span class="n"&gt;TeamState&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"team-search"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"new-team"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CreateTeam&lt;/span&gt; &lt;span class="n"&gt;OnSave&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"this.CreateTeam"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"search-area"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Search&lt;/span&gt; &lt;span class="n"&gt;OnSearch&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"state.Search"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"search-results"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SearchResults&lt;/span&gt; &lt;span class="n"&gt;Teams&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"state.TeamSearchResults"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="n"&gt;@code&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;OnInitializedAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnChange&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;StateHasChanged&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;CreateTeam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Team&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateTeam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReRunSearch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An instance of the TeamState object is injected into the page, and on PageLoad &lt;em&gt;(OnInitializedAsync is Blazor's PageLoad event)&lt;/em&gt; the state Blazor page StateHasChanged event is subscribed to the OnChange event of the state injected state class.&lt;/p&gt;

&lt;p&gt;So, when a search runs within the TeamState object the OnChange event is triggered. In this instance, the StateHasChanged event of the Blazor page will execute. Taking wording straight from the &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/blazor/lifecycle?view=aspnetcore-3.1#state-changes"&gt;Microsoft docs&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"StateHasChanged notifies the component that its state has changed. When applicable, calling StateHasChanged causes the component to be rerendered."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Binding any page to the OnChange event of the state will, therefore, cause the page to be re-rendered whenever our state components notify that something has changed. Cool!&lt;/p&gt;

&lt;h1&gt;
  
  
  Model Binding
&lt;/h1&gt;

&lt;p&gt;Now that we can retrieve data from the server and give that to the front end, it'd be fantastic to actually get it to display on the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Displaying data
&lt;/h2&gt;

&lt;p&gt;Sticking with the best front end practices I've taken from JS, my Teams.razor page handles all of the state management. It then passes the data down to components to manage the display.&lt;/p&gt;

&lt;p&gt;A good example of this is the SearchResults component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;col&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;row&lt;/span&gt; &lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;
&lt;/span&gt;        &lt;span class="nf"&gt;@if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Teams&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;Teams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;.&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;table&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;
&lt;/span&gt;                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;thead&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;th&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"col"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;th&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;th&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"col"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;th&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;thead&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;tbody&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nf"&gt;@foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Team&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Teams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;tr&lt;/span&gt; &lt;span class="n"&gt;@key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"team.Id"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;th&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/teams/@team.Id"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;@team&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;th&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;@team&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;tbody&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="n"&gt;@code&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IReadOnlyList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Team&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Teams&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above snippet raises one of the key parts of Blazor functionality. That being, the Parameter attribute.&lt;/p&gt;

&lt;p&gt;The parameter attribute indicates that a value can be passed into the component by way of an HTML element.&lt;/p&gt;

&lt;p&gt;In this case, there is a parameter that holds a list of Team objects.&lt;/p&gt;

&lt;p&gt;This component can certainly be classified as dumb.&lt;/p&gt;

&lt;p&gt;It takes a list of objects and either shows 'no results found', or creates an HTML table and loops through each Team object to create the table rows.&lt;/p&gt;

&lt;p&gt;C#. In the browser. It's a magical time to be a .NET developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a new record
&lt;/h2&gt;

&lt;p&gt;The final piece of this front end puzzle is creating new records.&lt;/p&gt;

&lt;p&gt;My absolute favourite feature of both Angular and React is the ability to be able to initialize a new instance of an object and bind that straight to an input in the HTML.&lt;/p&gt;

&lt;p&gt;Luckily, Blazor has taken notes from the JS big boys and offers a similar kind of function.&lt;/p&gt;

&lt;p&gt;For that, let's have a look at the CreatePlayer component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EditForm&lt;/span&gt; &lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;@newPlayer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;col&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;input&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;group&lt;/span&gt; &lt;span class="n"&gt;mb&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;form&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;control&lt;/span&gt;&lt;span class="s"&gt;" placeholder="&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="s"&gt;" aria-label="&lt;/span&gt;&lt;span class="n"&gt;Search&lt;/span&gt; &lt;span class="n"&gt;Term&lt;/span&gt;&lt;span class="s"&gt;" @bind="&lt;/span&gt;&lt;span class="n"&gt;newPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;input&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;group&lt;/span&gt; &lt;span class="n"&gt;mb&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;InputSelect&lt;/span&gt; &lt;span class="n"&gt;@bind&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;newPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Position&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;
&lt;/span&gt;                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"GK"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Goalkeeper&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"DEF"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Defender&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"MID"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Midfielder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"ST"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Striker&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;InputSelect&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;row&lt;/span&gt; &lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;ml&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;
&lt;/span&gt;                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt; &lt;span class="n"&gt;@onclick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"() =&amp;gt; OnSave.InvokeAsync(newPlayer)"&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;btn&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;danger&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;
&lt;/span&gt;                    &lt;span class="n"&gt;Add&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;EditForm&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;@code&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;EventCallback&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OnSave&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;TeamId&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Player&lt;/span&gt; &lt;span class="n"&gt;newPlayer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;OnInitializedAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;newPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TeamId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TeamId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnInitializedAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the page is loaded, a new Player object is created. OnInitialization, the TeamId of the newly created player object is assigned from an input value.&lt;/p&gt;

&lt;p&gt;A TeamId is a requirement for a player object, so it probably makes sense to implement some kind of null check on the TeamId property just in case. But that's a job for another day.&lt;/p&gt;

&lt;p&gt;From there, an EditForm component is added to the HTML. An EditForm parses out to a standard HTML form. However, it enables model binding between the DOM and the C# object.&lt;/p&gt;

&lt;p&gt;Using the &lt;a class="mentioned-user" href="https://dev.to/bind"&gt;@bind&lt;/a&gt; element, the HTML becomes reasonably straightforward. An input is bound to the player name property and a select dropdown is bound to the position property.&lt;/p&gt;

&lt;p&gt;When the form is submitted, the OnSave event callback is invoked.&lt;/p&gt;

&lt;p&gt;What the hell is an EventCallback?&lt;/p&gt;

&lt;p&gt;For that to make a little more sense, let's take a quick look at a component that consumes this CreatePlayer component.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TeamMetadata.razor&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="nf"&gt;@if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TeamData&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;@TeamData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Current&lt;/span&gt; &lt;span class="n"&gt;Players&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CreatePlayer&lt;/span&gt; &lt;span class="n"&gt;OnSave&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"OnAddPlayer"&lt;/span&gt; &lt;span class="n"&gt;TeamId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"@TeamId"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlayerList&lt;/span&gt; &lt;span class="n"&gt;Players&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"@TeamData.Players"&lt;/span&gt; &lt;span class="n"&gt;OnDelete&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"OnDeletePlayer"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;@code&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Team&lt;/span&gt; &lt;span class="n"&gt;TeamData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;TeamId&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;EventCallback&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OnAddPlayer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;EventCallback&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OnDeletePlayer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ViewTeam.razor&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;@page&lt;/span&gt; &lt;span class="s"&gt;"/teams/{TeamId}"&lt;/span&gt;

&lt;span class="n"&gt;@using&lt;/span&gt; &lt;span class="n"&gt;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logging&lt;/span&gt;
&lt;span class="n"&gt;@using&lt;/span&gt; &lt;span class="n"&gt;LeagueManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Teams&lt;/span&gt;

&lt;span class="n"&gt;@inject&lt;/span&gt; &lt;span class="n"&gt;TeamState&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TeamMetadata&lt;/span&gt; &lt;span class="n"&gt;TeamData&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"@teamData"&lt;/span&gt; &lt;span class="n"&gt;TeamId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"@TeamId"&lt;/span&gt; &lt;span class="n"&gt;OnAddPlayer&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"state.AddPlayer"&lt;/span&gt; &lt;span class="n"&gt;OnDeletePlayer&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"state.DeletePlayer"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;


&lt;span class="n"&gt;@code&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;TeamId&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Team&lt;/span&gt; &lt;span class="n"&gt;teamData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;OnInitializedAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnChange&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;StateHasChanged&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;teamData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSpecific&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TeamId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ConfigureAwait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;OnAfterRenderAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;firstRender&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;teamData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSpecific&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TeamId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ConfigureAwait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two code snippets above are for the View Team page, and the corresponding TeamMetadata component within it.&lt;/p&gt;

&lt;p&gt;Sticking with the practice of having as little components managing state as possible, the ViewTeam page holds all the logic for loading the players and the creation of a new player.&lt;/p&gt;

&lt;p&gt;The state.AddPlayer method is passed down into the OnAddPlayer parameter of the TeamMetadata component, and from there into the OnSave parameter of the CreatePlayer component.&lt;/p&gt;

&lt;p&gt;So when the save button is clicked in the CreatePlayer component that event bubbles all the way up to state.AddPlayer method. The newly created and data-bound Player object goes along for the ride and is sent off to our backend server for creation.&lt;/p&gt;

&lt;p&gt;I will say it again...&lt;/p&gt;

&lt;p&gt;C#. In the browser. It's a magical time to be a .NET developer.&lt;/p&gt;

&lt;h1&gt;
  
  
  In Summary
&lt;/h1&gt;

&lt;p&gt;Sometimes I can waffle a little bit with these summary sections. This time, not so much.&lt;/p&gt;

&lt;p&gt;Blazor is one of the best web technologies I have ever worked with. I'm probably biased, being a .NET developer. But I no longer need to stumble my way through the world of Javascript.&lt;/p&gt;

&lt;p&gt;And that my friends is a magical place to be.&lt;/p&gt;

</description>
      <category>blazor</category>
      <category>frontend</category>
      <category>webdev</category>
      <category>webassembly</category>
    </item>
    <item>
      <title>Refactoring with Clean Architecture - The magic of well-designed software</title>
      <dc:creator>James Eastham</dc:creator>
      <pubDate>Sun, 16 Feb 2020 21:31:34 +0000</pubDate>
      <link>https://dev.to/jeastham1993/refactoring-with-clean-architecture-the-magic-of-well-designed-software-1m99</link>
      <guid>https://dev.to/jeastham1993/refactoring-with-clean-architecture-the-magic-of-well-designed-software-1m99</guid>
      <description>&lt;p&gt;This week has been a pretty crazy one, with an AWS exam and two weddings in three days (one of which I was the best man) there hasn't been much mental capacity for Golang.&lt;/p&gt;

&lt;p&gt;So this post takes on a slightly shorter and more high-level discussion around software architecture and the huge benefits that come from doing it 'right'.&lt;/p&gt;

&lt;p&gt;Don't worry, there are still some lines of code. &lt;/p&gt;

&lt;h1&gt;
  
  
  Doing it right
&lt;/h1&gt;

&lt;p&gt;Much like a lot of things in life, I'm not the kind of person who believes his opinion is the only one and everyone should do the same way.&lt;/p&gt;

&lt;p&gt;Part of the fun in life is understanding other people's points of view, and putting together the best of different viewpoints.&lt;/p&gt;

&lt;p&gt;That's not to say I don't have opinions on software design though. Far from it. &lt;/p&gt;

&lt;p&gt;I'm a huge advocate of Uncle Bob Martin 'Clean Architecture' style of coding. Loosely related to &lt;a href="https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/"&gt;Jeffrey Palermo's Onion Architecture&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The basic rule being, that each layer of a software system should have no dependency on a layer outside of it.&lt;/p&gt;

&lt;p&gt;The core of the onion would be pure business entities. Coupled with Domain Driven Design, this gives you a central code base that is extremely well matched to the domain itself.&lt;/p&gt;

&lt;p&gt;No mention of SqlConnection's or HTTP calls. Pure, well-structured code.&lt;/p&gt;

&lt;p&gt;Outside of that, there are domain services or interactors. These are objects that have the sole responsibility of managing your domain objects. Business logic is likely to sit in this layer. &lt;/p&gt;

&lt;p&gt;Taking an example from the team-service repository in my &lt;a href="https://github.com/jeastham1993/football-league-manager-app"&gt;football league manager app git repo&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// CreateTeamRequest holds a reference to new team data.&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CreateTeamRequest&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// CreateTeamResponse holds a reference to new team data.&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CreateTeamResponse&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ID&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;   &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Errors&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// CreateTeam creates a new team in the database.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interactor&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TeamInteractor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;CreateTeam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;team&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;CreateTeamRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;CreateTeamResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;interactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Team name cannot be empty"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;CreateTeamResponse&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Errors&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Team name cannot be empty"&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Team name cannot be empty"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;newTeam&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Team&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;createdTeamID&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;interactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TeamRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newTeam&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;interactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EventHandler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"leaguemanager-info-newteamcreated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TeamCreatedEvent&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;TeamID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;createdTeamID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;TeamName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;CreateTeamResponse&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;createdTeamID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;newTeam&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CreateTeam method of the team interactor holds all the logic for creating a new team in the database. It takes a CreateTeamRequest as it's input, and returns a CreateTeamResponse.&lt;/p&gt;

&lt;p&gt;You'll notice the code references interactor.EventHandler and interactor.TeamRepository. These are interfaces that will, at some stage, be implemented with actual event handling and data storage code.&lt;/p&gt;

&lt;p&gt;As far as the business logic is concerned though, nothing changes.&lt;/p&gt;

&lt;p&gt;Going back to the title of the article, refactoring is made so much easier following this model.&lt;/p&gt;

&lt;p&gt;Any changes to the data storage provider have no effect AT ALL on the business logic itself. Conversely, business logic can be changed freely with ease.&lt;/p&gt;

&lt;p&gt;If I wanted to change exactly when the data was persisted to the repository, I could move the call to TeamRepository.Store and not worry about the details about how it actually happens.&lt;/p&gt;

&lt;p&gt;One of the best takeaways from Bob Martin's book on Clean Architecture is regarding the detail. The details (Database provider, event bus etc.) should be left to as late in the development process as possible.&lt;/p&gt;

&lt;p&gt;Often, people want to jump in with the database, then spend 2 months getting bogged down with schema.&lt;/p&gt;

&lt;p&gt;The alternative, write your code first abstracting the storage layer. Then add your database once you know more about the data you will be stored in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Event handling - An example
&lt;/h2&gt;

&lt;p&gt;Currently, the code for the event handler of the team service uses Amazon SNS and looks something like the below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;infrastructure&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"errors"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"strings"&lt;/span&gt;
    &lt;span class="s"&gt;"team-service/domain"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws/credentials"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws/session"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/service/sns"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// ErrTopicNotFound is returned when the requested topic is not found.&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ErrTopicNotFound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Specified topic not found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// AmazonSnsEventBus is an event bus implementation using Amaazon SQS.&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;AmazonSnsEventBus&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;svc&lt;/span&gt;       &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SNS&lt;/span&gt;
    &lt;span class="n"&gt;availableTopics&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// NewAmazonSnsEventBus creates a instance of the AmazonSnsEventBus.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewAmazonSnsEventBus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;AmazonSnsEventBus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Initialize a session that the SDK will use to load&lt;/span&gt;
    &lt;span class="c"&gt;// credentials from the shared credentials file ~/.aws/credentials&lt;/span&gt;
    &lt;span class="c"&gt;// and region from the shared configuration file ~/.aws/config.&lt;/span&gt;
    &lt;span class="n"&gt;sess&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Region&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"eu-west-1"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Credentials&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewSharedCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"league-manager-sqs"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}))&lt;/span&gt;

    &lt;span class="n"&gt;svc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;availableTopics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListTopics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;availableTopicArns&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;availableTopics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Topics&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;availableTopics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Topics&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;availableTopicArns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TopicArn&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;AmazonSnsEventBus&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;availableTopics&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;availableTopicArns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Publish sends a new message to the event bus.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ev&lt;/span&gt; &lt;span class="n"&gt;AmazonSnsEventBus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;publishTo&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;evt&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;requiredTopicArn&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;ev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;availableTopics&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;publishTo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;requiredTopicArn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requiredTopicArn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PublishInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;evt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsEvent&lt;/span&gt;&lt;span class="p"&gt;())),&lt;/span&gt;
            &lt;span class="n"&gt;TopicArn&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requiredTopicArn&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Event published: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MessageId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ErrTopicNotFound&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since learning more about AWS services, I've actually discovered that Amazon Kinesis is a better tool than SNS for my exact use case.&lt;/p&gt;

&lt;p&gt;Instead of needing to pick my way through the entire code base looking for references to Amazon SNS. I just need to write a new implementation of the event handler interface that uses Kinesis.&lt;/p&gt;

&lt;p&gt;On application startup, I just decide which event handler to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// teamInteractor.EventHandler = new(infrastructure.MockEventBus)&lt;/span&gt;
&lt;span class="c"&gt;// teamInteractor.EventHandler = infrastructure.NewAmazonSnsEventBus()&lt;/span&gt;
&lt;span class="n"&gt;teamInteractor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EventHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;infrastructure&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewAmazonKinesisEventBus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No changes to business logic. No changes to domain objects.&lt;/p&gt;

&lt;p&gt;One single line of code changed, and all that's changed is the infrastructure.&lt;/p&gt;

&lt;p&gt;The chances of a bug being introduced are almost non-existent (granted there may be bugs in the new code that gets written).&lt;/p&gt;

&lt;p&gt;So whilst I'm not saying this is the only way to build well-designed software, and it certainly does have flaws. I hope it has shown that putting time into the design pays out massive dividends in the future.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>design</category>
    </item>
    <item>
      <title>How do you monitor the usage of your API endpoints?</title>
      <dc:creator>James Eastham</dc:creator>
      <pubDate>Wed, 12 Feb 2020 09:56:43 +0000</pubDate>
      <link>https://dev.to/jeastham1993/how-do-you-monitor-the-usage-of-your-api-endpoints-p73</link>
      <guid>https://dev.to/jeastham1993/how-do-you-monitor-the-usage-of-your-api-endpoints-p73</guid>
      <description>&lt;p&gt;EDIT: so I'm now thinking of releasing a cloud app that can be used to monitor API usage. Any interested beta testers drop me a message below :-)&lt;/p&gt;

&lt;p&gt;Day to day I develop a reasonably large web API used by quite a few different clients. It was put together almost 2 years ago now when I had very little consideration of monitoring or auditing.&lt;/p&gt;

&lt;p&gt;My current focus is on the usage of certain API endpoints. What is your practice for auditing the usage of a certain endpoint?&lt;/p&gt;

&lt;p&gt;For context, I'ts a .NET API and the API is distributed across multiple non-related clients systems. Ideally I'd like to push the results to ElasticSearch.&lt;/p&gt;

&lt;p&gt;I feel like a custom piece of middleware is the best option, but am a little concerned about performance.&lt;/p&gt;

&lt;p&gt;Thoughts?&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>CI/CD in GoLang with Azure Dev Ops and AWS Elastic Beanstalk</title>
      <dc:creator>James Eastham</dc:creator>
      <pubDate>Sun, 09 Feb 2020 18:32:46 +0000</pubDate>
      <link>https://dev.to/jeastham1993/ci-cd-in-golang-with-azure-dev-ops-and-aws-elastic-beanstalk-2cel</link>
      <guid>https://dev.to/jeastham1993/ci-cd-in-golang-with-azure-dev-ops-and-aws-elastic-beanstalk-2cel</guid>
      <description>&lt;p&gt;&lt;em&gt;This post follows a series of articles I've been writing about designing a distributed system. I'd highly recommend at least ready &lt;a href="https://dev.to/jeastham1993/design-build-deploy-series-introduction-18kb"&gt;part 1&lt;/a&gt; before this one just to get a little bit of context.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now that the team-service application is as feature-rich as it needs to be for the moment, it feels like a great time to get it running somewhere more useful to the world. &lt;a href="http://localhost:8000"&gt;http://localhost:8000&lt;/a&gt; is fantastic for me, not so great for anybody else who wants to use the app.&lt;/p&gt;

&lt;p&gt;With the tooling we have available as developers today, I see no reason at all that software should ever be manually deployed.&lt;/p&gt;

&lt;p&gt;For that reason, the team-service application is going to be deployed using CI/CD pipelines that require 0 user interaction. All releases should be triggered using a developer-friendly interaction (git commit, pull request etc).&lt;/p&gt;

&lt;p&gt;There are a couple of different tools I'm going to be using to achieve this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://azure.microsoft.com/en-us/services/devops/"&gt;Azure Dev Ops Services&lt;/a&gt; - Azure Dev Ops is by far my favourite build and release pipeline provider. It can also include built-in git repos for those private projects you want to keep away from prying eyes. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/elasticbeanstalk/"&gt;AWS Elastic Beanstalk&lt;/a&gt; - AWS Elastic Beanstalk is a fantastic offering for quickly getting source code running in a public domain.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Some of you may be questioning why I'm using a Microsoft provided CI/CD service with Amazon provided infrastructure.&lt;/p&gt;

&lt;p&gt;My biggest reason is the simplicity of using build pipelines in Azure Dev Ops. I've experimented with AWS Code Build and don't find the interface anywhere near as intuitive.&lt;/p&gt;

&lt;p&gt;Deploying to AWS from Azure Dev Ops is extremely easy with the install of an extension from the marketplace.&lt;/p&gt;

&lt;p&gt;There is no clear cut reason why I prefer AWS over Azure for app hosting. They are both fantastic cloud providers.&lt;/p&gt;

&lt;p&gt;I work with AWS a lot more in my day to day work, and I find their costs for playing around to be a lot more reasonable. Hence the reasoning for pushing to AWS.&lt;/p&gt;

&lt;h1&gt;
  
  
  Build Pipelines in Azure Dev Ops
&lt;/h1&gt;

&lt;p&gt;Build pipelines in Azure Dev Ops use yaml. You can either manually type out the yaml files, are use the editor within the UI (which is fantastic by the way). &lt;/p&gt;

&lt;p&gt;To get started though, I need to link a new project to my GitHub repo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When creating the new project, it's important to create it as public. You can only link to a public GitHub repo from a public Azure Dev Ops project.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flk2pcuegwplcoy2is5pu.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flk2pcuegwplcoy2is5pu.PNG" alt="Alt Text" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Dev Ops UI makes it extremely easy to link up a GitHub project. On creation of a new pipeline, just step through the wizard to create a link to the GitHub repo.&lt;/p&gt;

&lt;p&gt;For the moment, I'm going to create a new pipeline and save the standard layout. It'll look something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Starter pipeline&lt;/span&gt;
&lt;span class="c1"&gt;# Start with a minimal pipeline that you can customize to build and deploy your code.&lt;/span&gt;
&lt;span class="c1"&gt;# Add steps that build, run tests, deploy, and more:&lt;/span&gt;
&lt;span class="c1"&gt;# https://aka.ms/yaml&lt;/span&gt;

&lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;

&lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;vmImage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ubuntu-latest'&lt;/span&gt;

&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo Hello, world!&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Run&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;one-line&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;script'&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;echo Add other tasks to build, test, and deploy your project.&lt;/span&gt;
    &lt;span class="s"&gt;echo See https://aka.ms/yaml&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Run&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;multi-line&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;script'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will commit a new file named azure-pipelines.yml to my git repo. Before I go any further, I'm going to do a little bit of housekeeping and move that file into the src folder for the team-service.&lt;/p&gt;

&lt;p&gt;Manually starting a build gives the following output.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjrpnu1xz0za3gl94i77y.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjrpnu1xz0za3gl94i77y.PNG" alt="Alt Text" width="800" height="311"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fantastic, we have a working build pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Required deployment steps
&lt;/h2&gt;

&lt;p&gt;Before adding any steps to the pipeline, it's important to think about exactly what the steps would be to deploy the code. In this instance:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run unit tests&lt;/li&gt;
&lt;li&gt;Package application in a runnable format (Zip file containing Dockerfile)&lt;/li&gt;
&lt;li&gt;Deploy to Elastic beanstalk environment&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simple, right.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Build Pipeline
&lt;/h2&gt;

&lt;p&gt;As much as I love writing code, the build pipeline UI really is too good to not use. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fduvkaqwr6xara3cohrnk.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fduvkaqwr6xara3cohrnk.PNG" alt="Alt Text" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can search for the required component on the right-hand side using the assistant, fill in the variables and add it to the yaml. Once finished, saving the pipeline commits it directly back to the Git repo. Magical!&lt;/p&gt;

&lt;p&gt;An important note at this point, I had already installed the &lt;a href="https://marketplace.visualstudio.com/items?itemName=AmazonWebServices.aws-vsts-tools"&gt;AWS Azure Dev Ops Extensions&lt;/a&gt;. It's a fantastic set of tools that makes deploying to AWS a piece of cake.&lt;/p&gt;

&lt;p&gt;So, back to that pesky YAML file. Here it is in its entirety, you'll find some explanation after the code block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 1. Triggers&lt;/span&gt;
&lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;releases/*&lt;/span&gt;
  &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;src/team-service/*&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Jobs&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;run_unit_tests&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Run&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Unit&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Test'&lt;/span&gt;
  &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;vmImage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ubuntu-latest'&lt;/span&gt;
&lt;span class="c1"&gt;# 3. Steps&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Run domain level tests&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Go@0&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test'&lt;/span&gt;
      &lt;span class="na"&gt;workingDirectory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;src/team-service/domain'&lt;/span&gt;
  &lt;span class="c1"&gt;# Run use case level tests&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Go@0&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test'&lt;/span&gt;
      &lt;span class="na"&gt;workingDirectory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;src/team-service/usecases'&lt;/span&gt;
  &lt;span class="c1"&gt;# Ensure application can build&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Go@0&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;build'&lt;/span&gt;
      &lt;span class="na"&gt;workingDirectory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;src/team-service/'&lt;/span&gt;


&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;package_application_files&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Package&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;application&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;files'&lt;/span&gt;
&lt;span class="c1"&gt;# 4. Dependent jobs&lt;/span&gt;
  &lt;span class="na"&gt;dependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;run_unit_tests&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'))&lt;/span&gt;
  &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;vmImage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ubuntu-latest'&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Create zip file of team-service&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ArchiveFiles@2&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;rootFolderOrFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;src/team-service'&lt;/span&gt;
      &lt;span class="na"&gt;includeRootFolder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;archiveType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;zip'&lt;/span&gt;
      &lt;span class="na"&gt;archiveFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$(Build.ArtifactStagingDirectory)/app.zip'&lt;/span&gt;
      &lt;span class="na"&gt;replaceExistingArchive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublishBuildArtifacts@1&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;PathtoPublish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$(Build.ArtifactStagingDirectory)'&lt;/span&gt;
      &lt;span class="na"&gt;ArtifactName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;drop'&lt;/span&gt;
      &lt;span class="na"&gt;publishLocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Container'&lt;/span&gt;
      &lt;span class="na"&gt;replaceExistingArchive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've added four numbered points within the YAML for ease of explanation.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Triggers
&lt;/h3&gt;

&lt;p&gt;Triggers allow control of when the pipeline should be triggered. In this case, I'm going to run it on any changes to master, or to any changes to a branch that begins with release/.&lt;/p&gt;

&lt;p&gt;The pipeline will also only run if the changes are made to any files under the src folder. This stops un-necessary builds when non-relevant changes are made (documentation, other services etc).&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Jobs
&lt;/h3&gt;

&lt;p&gt;Jobs are a way of organizing a pipeline into logical steps. Each pipeline will have at least one job, and each job can have any number of steps.&lt;/p&gt;

&lt;p&gt;In this case, there are two jobs. One to run the unit tests and one to package up and deploy the application files.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Steps
&lt;/h3&gt;

&lt;p&gt;Steps make up the meat of the pipeline functionality. They are the individual tasks that will happen, one by one, against your source code. In this case, we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run the domain level unit tests&lt;/li&gt;
&lt;li&gt;Run the usecase level unit tests&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ensure the entire go application can build&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ZIP the application files inc. Dockerfile&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Publish the build artifact&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No mention of AWS just yet, more on that in just a second.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Dependent jobs
&lt;/h3&gt;

&lt;p&gt;One of the most powerful features of the pipelines is controlling which jobs run conditionally.&lt;/p&gt;

&lt;p&gt;Here, I'm only running the second job if the source branch begins with release and if the run_unit_tests job completes successfully.&lt;/p&gt;

&lt;p&gt;And there we have it, 53 lines of code to test, build and package a Go application. Now for the deployment part:&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy from Azure Dev Ops to AWS
&lt;/h2&gt;

&lt;p&gt;Azure Dev Ops splits it's pipelines functionality into two seperate distinct parts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Builds &lt;em&gt;The generation of 'artifacts' containing packaged versions of the source code&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Release &lt;em&gt;Uses build artifacts and deploys them&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this section, I'll run through the extremely simple setup of a release pipeline to push to AWS. First though, some quick AWS admin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create an Elastic Beanstalk Application
&lt;/h3&gt;

&lt;p&gt;For the release to work, we first need an elastic beanstalk application and environment to deploy to.&lt;/p&gt;

&lt;p&gt;For that, log in to the AWS management console and head over to the elastic beanstalk section.&lt;/p&gt;

&lt;p&gt;From there, create a new application with a descriptive name.&lt;/p&gt;

&lt;p&gt;Once within the created application, you will want to provision a new environment. I tend to run with two separate environments for most applications; dev and prod.&lt;/p&gt;

&lt;p&gt;For this application here, I'm going to create a new dev environment using Docker as the runtime. For the time being, I also want to create the environment using sample application code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Feat8wna2j0cux1q5nnuh.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Feat8wna2j0cux1q5nnuh.PNG" alt="Alt Text" width="800" height="657"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Following the same steps to create another environment named production, after a few minutes I end up with the following in the Elastic Beanstalk application interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F557loxsswcn1hmugq287.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F557loxsswcn1hmugq287.PNG" alt="Alt Text" width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One final AWS console step, create a publically accessible Amazon S3 bucket. For that, head off to the S3 interface and create a new bucket. Ensuring the block public access options are disabled.&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure Dev Ops Release Pipeline
&lt;/h3&gt;

&lt;p&gt;There are a million and one different ways to manage release pipelines. Push direct to production. Push all builds to development but only certain builds to production. The list is endless.&lt;/p&gt;

&lt;p&gt;I'm going to start with the following flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The developer creates a new branch from a master named release/&lt;em&gt;&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Build pipeline triggered from the release branch&lt;/li&gt;
&lt;li&gt;Release pipelines push to development, and waits for a specified number of minutes before pushing to production. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This gives the opportunity for a quick test of the development environment before the change gets automatically sent to production.&lt;/p&gt;

&lt;p&gt;I also considered having an approval gate to allow a specific user to approve the release, but given I'm the only developer right now that seemed useless.&lt;/p&gt;

&lt;p&gt;I'm going to head into the Releases section of the Azure Dev Ops UI and create a new release pipeline. Initially, I want to configure from an empty pipeline.&lt;/p&gt;

&lt;p&gt;All release pipelines need to be linked up to a build artifact, for that, I'm going to add a new artifact and link it to the build pipeline created earlier.&lt;/p&gt;

&lt;p&gt;I then need 3 distinct steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Push to development&lt;/li&gt;
&lt;li&gt;Wait for X minutes&lt;/li&gt;
&lt;li&gt;Push to production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Luckily, all that is nice and simple.&lt;/p&gt;

&lt;p&gt;The two push tasks are almost identical, so here is the detail from the development one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ft3xmk5tmk8osy2b2ts78.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ft3xmk5tmk8osy2b2ts78.PNG" alt="Alt Text" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, I take the ZIP file generated from the build pipeline and upload that to the S3 bucket created at the start of this section. &lt;/p&gt;

&lt;p&gt;For the moment, I standardize the zip file to always be named app.zip, but in the future, I would like to upload a zip file named with the release number.&lt;/p&gt;

&lt;p&gt;From there, I create a new Elastic Beanstalk deployment using the zip file uploaded to S3.&lt;/p&gt;

&lt;p&gt;Elastic beanstalk is smart enough to know, because I set the platform to be Docker, that it needs to unpack the ZIP file and look for a Dockerfile. Using that Dockerfile, EB can generate an image and run it right away.&lt;/p&gt;

&lt;p&gt;Cloud computing... it's a magical place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5vvhadw22qpqsms9dc8n.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5vvhadw22qpqsms9dc8n.PNG" alt="Alt Text" width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is my complete release pipeline. A push to development, a nice simple wait task and then a push to production.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;There we have it, a complete end to end deployment pipeline from development machine through to a live application running in AWS.&lt;/p&gt;

&lt;p&gt;As a test, I've run the following commands from my local machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; release/1.0.0
git push &lt;span class="nt"&gt;--set-upstream&lt;/span&gt; origin release/1.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sure enough, my build pipeline runs to completion before starting my release pipeline.&lt;/p&gt;

&lt;p&gt;A few minutes later, the application is running in production.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6u4hzc32zxyul1qudurn.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6u4hzc32zxyul1qudurn.PNG" alt="Alt Text" width="800" height="69"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've said it once, and I'll say it again, cloud computing is truly a magical place.&lt;/p&gt;

&lt;p&gt;This is by no means a production-ready guide right now, the main reason being that both development and production are running using the same underlying infrastructure (Dynamo DB and SQS queues).&lt;/p&gt;

&lt;p&gt;Application configuration, now there's a conversation for another day.&lt;/p&gt;

&lt;p&gt;As always, feedback is greatly appreciated.&lt;/p&gt;

</description>
      <category>go</category>
      <category>azuredevops</category>
      <category>aws</category>
      <category>continuousdeployment</category>
    </item>
  </channel>
</rss>
