GithubHelp home page GithubHelp logo

davidajohn / fotostoriomicroservices Goto Github PK

View Code? Open in Web Editor NEW
23.0 3.0 4.0 1.9 MB

.NET application built using a microservice architecture with Docker containers. Includes a Blazor WebAssembly e-commerce store with Stripe Elements payment integration.

C# 65.46% Dockerfile 1.40% HTML 28.83% CSS 1.97% JavaScript 0.72% TSQL 0.60% TypeScript 1.02%
dotnet docker blazor blazor-webassembly blazor-server tailwindcss stripe pci-dss jsinterop microservices

fotostoriomicroservices's Issues

Exception Middleware

There are several Services projects that have controllers which contain try...catch logic. It makes me feel a little uneasy seeing 500 errors being returned in so many of the catch sections.

I'd like to get rid of that repetitive code and use some form of exception middleware instead, which would make the controllers simpler and centralise exception handling and logging.

Something like Jason Watmore's solution would be ideal: https://jasonwatmore.com/post/2022/01/17/net-6-global-error-handler-tutorial-with-example

Health Checks failing & missing icons

There are a couple of unrelated issues with the Application Status project which uses the built-in .NET health checks generated by each project:

  1. There is a cosmetic issue where a particular font isn't being loaded after the update to .NET 8. It appears to be a relatively simple fix involving the webpack config. There is an open issue containing a PR waiting for the contributor to agree to the CLA. I'll leave it for now as this isn't vital. It's just to show the icons in the UI.
  2. The projects that have been updated to .NET 8 are all showing as being down (when they are not). Is it related to the default port changing to 8080 from 80? Microsoft's docs on Health Checks are here

Update to .NET 8

It's time to gradually update the application to .NET 8, even if it's just a case of switching to the new v8 libraries for now and implementing new features later.

Anything that can help improve speed and reduce the memory footprint of each microservice has to be a good thing.

Extending Serilog and Seq to all projects

Although Serilog is now being used to log to a Seq instance from the vital backend services like the Products API and the Ordering API, having done that it feels like the entire application should probably be doing the same as well.

Seq's UI is fantastic for filtering logs by application/service (among many other things), so although we may not need the full power of Serilog's structured logging of objects for the other services, there is still a lot to be said for having all logs available in one place, especially for an application with this kind of microservice architecture.

Use of AutoMapper in Discount Grpc

While updating the version of AutoMapper used in parts of the application (in order to switch to the non-DI v13> package), it occurred to me that perhaps the Discount Grpc project should be using a custom mapper class instead - which should be quicker (but slightly less convenient).

Otherwise, we're potentially losing part of the speed advantage that Grpc offers.

Adding an Ordering/Marketing Aggregator?

While planning the features for a simple client application for a fictional Marketing department, I'm seeing scenarios where there will be a need to combine data from orders, discounts and marketing campaigns.

I'm considering whether there would be value in adding another API project that aggregates that information.

For example: if you wanted to see whether a marketing campaign generated additional sales, you would want to see if the number of orders increased during that period and how many of the orders placed during that campaign included discounted products.

But will there be a sufficient number of instances where we need that combined data? Alternatively, if we simply get data from different sources and combine them inside a client app, is it complex and/or slow to accomplish the functionality that we want?

Let's take the example I mentioned above - if we have a client application with a campaigns page that can show orders made during a campaign, we can simply have an order service that gets orders within the campaign's start and end dates. That is not sufficiently complex to justify the additional aggregator project.

Are there potentially more complex scenarios that would justify the additional work though? What if there was a requirement for more complex data analysis, like breaking down sales by brands or models?

I don't have a definitive answer to that question at the moment, and the decision will probably be influenced mostly based on my experience while adding features to this marketing client app.

In a real life scenario, I would probably wait to see how the requirements of the marketing department staff evolved and only if they reached a level of complexity that made client app development onerous would I look at adding an aggregator API. Or if we could demonstrably see bottlenecks when combining data within the app.

Basket API and AutoMapper

The Basket API also uses AutoMapper when updating (or POSTing to) a customer basket.

It uses a call to the Discount Grpc service to retrieve any current discounts for a particular product when it is added to a basket.

Testing in Postman, I found that with 2 items in a basket the request was completing in anywhere from 125ms to 288ms.

It would be interesting to see how much could be shaved off that by using what would be a very simple, one-way custom mapper class instead of AutoMapper.

Originally posted by @DavidAJohn in #23 (comment)

Ordering API returning 404 to admin orders page

While updating the Admin.BlazorServer UI project, I noticed the admin page which shows customer orders provoked a 404 from the Ordering API (via the admin gateway) because there are no orders in the database (or none in the last x days).

I'm not sure this is the correct response when there are no orders to return, but there can be huge debates over this.

Ultimately, what does the CustomerOrders.razor page need to do? Is there value in returning a different response when there are no orders to return, as opposed to when there is a problem retrieving the orders?

Breaking changes in .NET 8 Docker images

There are some breaking changes that have been introduced with the .NET 8 Docker images.

While testing, I found that several services that had been updated to .NET 8 where no longer responding, although no errors where visible in the logs. I quickly discovered that I'd missed a memo about the default port changing from 80 to 8080.

There are some other changes as well, which Andrew Lock has explained excellently, as ever:

https://andrewlock.net/exploring-the-dotnet-8-preview-updates-to-docker-images-in-dotnet-8/

Also here's a practical example of a .NET 8 app that uses docker compose:

https://www.yogihosting.com/docker-compose-aspnet-core/

Serilog and Seq

An addition that I'd like to make to the application is to add Serilog to at least some of the services - definitely to the Ordering API and the Products API at least.

This would probably only need a few additional nuget packages and perhaps some minor alterations to the logging statements. Sinks for the console and Seq would likely be sufficient.

I'll then add a Seq container instance to the application based on the official Docker image.

Migrate gateways from Ocelot to YARP?

It's no secret that Ocelot is no longer being actively developed and Tom Pallister himself has suggested people use alternatives like YARP, although he has said he will update Ocelot with each new version of .NET.

FotoStorio has several gateway projects that currently use Ocelot for routing requests to different services on the back end. They all function without any problems, but YARP is probably a better long-term option given that it is actively maintained by Microsoft and used internally by them.

I'll try creating a gateway using YARP instead of Ocelot to see how the amount of work involved stacks up against the benefits moving forward. I would imagine there may be speed and memory usage benefits to using YARP, as well as the obvious future-proofing.

Time to consider .NET 7

It's probably time to start thinking about which projects would benefit from being upadted to .NET 7.

Anything involving Entity Framework should be stringly considered, given the performance gains.

Also the Discount minimal API would be a good candidate.

Integration Tests using SQL Server and EF

After quite a bit of experimentation, I've hit a brick wall trying to write integration tests that run against a real instance of SQL Server (or at least, a 'real' SQL Server instance created using Testcontainers from inside a WebApplicationFactory).

Whatever I try, I always end up back in the same situation where I get an error from Entity Framework about the entity already being tracked. I've tried just about every suggestion I've found on Stack Overflow and elsewhere, and the outcome always seems to be the same. There comes a point where everyone stops trying to get this to work and looks at an alternative involving an In Memory database and/or SQLite.

For reference, here's an example from Microsoft's own sample projects.

I'm not particularly keen on that approach because you're not testing against the actual database and there are issues with LINQ.

Microsoft have an absolutely insane suggestion which involves running tests inside a transaction and then rolling it back afterwards, to avoid any commits being made to the database. That seems like a tacit admission that there is no way around these EF tracking issues. It also just relies on an existing database, rather than having some kind of automatic creation and teardown.

Here's a potential alternative which uses the Docker.DotNet library:

.NET Core Integration Tests using a Sql Server Database in Docker

Update all projects to .NET 6

At the moment, the application consists of a mixture of .NET 5 and .NET 6 projects.

The Blazor client apps are .NET 6, but most of the backend services are still .NET 5.

It's not a huge problem, but it would be nice to have everything consist, especially as .NET 6 is the LTS release.

The first step would be updating the version numbers in the .csproj files and testing that each project still builds successfully.

Product details and Inventory

The store's product details page currently has a hard-coded limit of 5 items.

The idea of allowing more than one to be bought is obviously a little contrived for a store that sells high-cost goods like cameras. Plus, why would anybody buy two of the same camera/lens?

But, given that we have an inventory API with current stock for each product, it would be good to link this up and set the limit to however many are in stock.

Stripe API updates

The Blazor store project (Store.BlazorWasm) currently uses v39 of the Stripe SDK. The latest version is v45.

However, each version of the SDK is tied to a particular version of Stripe's API, which can be selected in the Stripe dashboard. You have a 72 hour window in which you can revert back to your previous API version if you update it.

In order to move to v40> of the SDK, I would need to update the API as well. There are quite a few breaking changes that have been introduced and while on the face of it there don't appear to be any changes that affect FotoStorio, it's always better to be safe than sorry with something like this.

This came to light because v39 of the Stripe SDK installs a version of Newtonsoft.Json that is now considered vulnerable (v12.0.3).

I may just update Newtonsoft.Json itself to a non-vulnerable version for the time being, then I'll look at updating the Stripe SDK and API at a later time.

Marketing client application

It was always my intention to create a simple client application that enabled the creation of marketing campaigns and discounts.

There is a marketing gateway already built and the backend functionality is available in both the Discount minimal API project and the Discount gRPC project.

It really only needs to give a fictional marketing dept the ability to create a campaign with start and end dates which includes a selectable list of discounted products.

I could build something using Blazor, but I'd always wanted to demonstrate how to add something different to the mix, so the feasible options for me are:

Blazor (Wasm or Server)
Angular
Next.Js
Vue/Nuxt

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.