matthewcorven / DaySpaPet

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

DaySpaPet

What

My spouse is a pet groomer and small business owner. I work and play with software engineering, and have spent the last 10 years repeatedly rewriting a full pet grooming system for this line of business and clients, targetting POS and mobile/web. Every attempt has been wildly different than the previous, changing designs & languages or frameworks based on the latest tech I've needed to learn for work, or something I watched on an NDC talk that inspired me.

This iteration is no more likely to continue than the others, but it occurred to me that this pursuit might be useful to others while also demonstrating where my headspace is. So I'm open-sourcing this project, and will be documenting my journey here. Please don't put your hopes into using this for your own grooming business, it's just for my own learning and fun. Discussion is welcomed, but I am not taking issues of any kind at this time.

Piecemeal Origins

I've tried my very best to capture here each influence this "project" has to credit. If I've missed some, please reach out in the public discussion area.

TODO: Future Stuff

Physical Architecture

  • Client-side platforms
    • Web Browser (evergreen)
    • Progressive Web App (PWA)
    • Native Windows, macOS, iOS, Android
  • Server-side
    • REST API application, packaged into a container
    • Uses HTTP response headers and shared persistent cache for mimimizing undue impact on database
    • Uses shared database offering NoSQL-like features as well as relational
    • Asynchronous background work for report processing, image processing and persistence, notifications, and more - all fault-tolerant with capability for scaling out.

Physical Integration Points

  1. Receipt printer
    • Epson TM-T88V, USB direct to same device as native client-side app, or ethernet direct on same network as client-side app.
  2. Document printer
    • Any pre-configured (OS-managed) local or network-attached document printer
  3. Barcode scanner
    • Any HID-compliant USB scanner
  4. Webcam (for client pet photos)
    • Any pre-installed webcam
  5. Cash drawer
    • APG brand, USB direct to same device as native client-side app

Design Decisions

  • .NET 8 MAUI cross-platform client-side applications, implemented with shared UI implemented in client-side Blazor.
    • Vertical Architecture (per page/route)
    • Fluent UI Blazor Components
  • .NET 8 Minimal API for server-side REST API
    • Clean Architecture (Ardalis style)
  • Leverage cloud platforms (with local alternatives for development) for:
    • Databases
    • Binary, NoSQL, & large text storage
    • Email notifications
    • SMS notifications

Value-Driven Dependencies

Package Name Value Repository Source License - Server-side API Shared UI WebAssembly Blazor (browser) WebAssembly Blazor (PWA) Windows macOS iOS Android
Bogus Realistic, deterministic, text-based git-friendly maintainability, and LARGE dev dataset. nuget github MIT - Yes
Hangfire In-process background work runtime orchestration and management, with persistent memory datastore for resilience. Provides fire-and-forget use cases as well as scheduling and outbox pattern initiator. nuget github LGPL v3 - Yes
MediatR In-process CQRS and Mediator pattern framework. nuget github LICENSE_NAME - Yes
MassTransit Event-driven behavior abstraction, leveraging providers such as Redis or Redis-inspired. nuget github Apache License v2 - Yes
RabbitMQ.Client nuget github Apache License v2 + Mozilla Public License v2.0 - Yes
MassTransit.RabbitMQ Messaging and streaming broker, packaged for integration with MassTransit nuget github Apache License v2 - Yes
Twilio Email & SMS notification PaaS with support for campaigns, etc. nuget github LICENSE_NAME - Yes
Redis nuget github LICENSE_NAME - Yes
FusionCache In-memory and/or persistent caching with a uniform development API across usage scenarios. nuget github LICENSE_NAME - Yes
Serilog Structured logging industry standard nuget github LICENSE_NAME - Yes
Fluent UI Blazor Components Great UI component library, layout + theme provider, and more. nuget github Apache License v2 - Yes Yes Yes Yes Yes Yes Yes

Project Reference Map

Project/Nuget Namespace CPM Version DaySpaPet.WebApi.Api DaySpaPet.WebApi.Core DaySpaPet.WebApi.Infrastructure DaySpaPet.WebApi.SharedKernel DaySpaPet.WebApi.UseCases DaySpaPet.WebApp.Base DaySpaPet.NativePlat.Client DaySpaPet.WebApp.Wasm
Ardalis.GuardClauses 4.5.0 - Inherit CPM - Inherit CPM - - - -
Ardalis.ListStartupServices 1.1.4 Inherit CPM - - - - Inherit CPM - -
Ardalis.Result 8.0.0 Inherit CPM Inherit CPM - - Inherit CPM Inherit CPM - -
Ardalis.Result.AspNetCore 8.0.0 Inherit CPM - - - - - - -
Ardalis.SmartEnum 8.0.0 - Inherit CPM - - - - - -
Ardalis.Specification 8.0.0 - Inherit CPM - Inherit CPM - - - -
Ardalis.Specification.EntityFrameworkCore 8.0.0 - - Inherit CPM - - - - -
Bogus 35.5.0 - - Inherit CPM - - - - -
CommunityToolkit.Maui N/A - - - - - - Inherit CPM -
FastEndpoints 5.25.0 Inherit CPM - - - - - - -
FastEndpoints.ApiExplorer 2.3.0 Inherit CPM - - - - - - -
FastEndpoints.Swagger 5.25.0 Inherit CPM - - - - - - -
FastEndpoints.Swagger.Swashbuckle 2.3.0 Inherit CPM - - - - - - -
MailKit 4.5.0 - - Inherit CPM - - - - -
MediatR 12.2.0 Inherit CPM Inherit CPM - Inherit CPM Inherit CPM - - -
Microsoft.AspNetCore.Components.Web N/A - - - - - Inherit CPM - -
Microsoft.AspNetCore.Components.WebAssembly N/A - - - - - - - Inherit CPM
Microsoft.AspNetCore.Components.WebAssembly.DevServer N/A - - - - - - - Inherit CPM
Microsoft.AspNetCore.Http.Abstractions 2.2.0 - - - - Inherit CPM - - Inherit CPM
Microsoft.AspNetCore.Mvc.NewtonsoftJson 8.0.4 Inherit CPM - - - - - - -
Microsoft.EntityFrameworkCore.Design N/A - - - - - Inherit CPM - -
Microsoft.EntityFrameworkCore.Relational 8.0.4 - - Inherit CPM - Inherit CPM - - -
Microsoft.EntityFrameworkCore.SqlServer 8.0.1 - - Inherit CPM - - - - -
Microsoft.EntityFrameworkCore.Tools 8.0.4 Inherit CPM - Inherit CPM - - - - -
Microsoft.Extensions.Configuration 8.0.0 - - Inherit CPM - - - - -
Microsoft.Extensions.Configuration.Binder N/A - - - - - - Inherit CPM -
Microsoft.Extensions.Configuration.Json N/A - - - - - - Inherit CPM -
Microsoft.Extensions.Logging 8.0.0 - - Inherit CPM - - - - -
Microsoft.Extensions.Logging.Abstractions 8.0.1 - Inherit CPM - Inherit CPM - - - -
Microsoft.Extensions.Logging.Debug N/A - - - - - - Inherit CPM -
Microsoft.FluentUI.AspNetCore.Components N/A - - - - - Inherit CPM Inherit CPM Inherit CPM
Microsoft.FluentUI.AspNetCore.Components.Emoji N/A - - - - - Inherit CPM Inherit CPM Inherit CPM
Microsoft.FluentUI.AspNetCore.Components.Icons N/A - - - - - Inherit CPM Inherit CPM Inherit CPM
Microsoft.Maui.Controls N/A - - - - - - Inherit CPM -
Microsoft.Maui.Core N/A - - - - - - Inherit CPM -
Microsoft.Maui.Essentials N/A - - - - - - Inherit CPM -
Microsoft.Maui.Resizetizer N/A - - - - - - Inherit CPM -
Microsoft.VisualStudio.Azure.Containers.Tools.Targets 1.20.1 Inherit CPM - - - - - - -
Microsoft.VisualStudio.Web.CodeGeneration.Design 8.0.2 Inherit CPM - - - - Inherit CPM - -
NodaTime 3.1.11 - Inherit CPM - Inherit CPM - - - -
NodaTime.Bogus 3.0.2 - - Inherit CPM - - - - -
Serilog.AspNetCore 8.0.1 Inherit CPM - - - - - - -
Serilog.Sinks.ApplicationInsights 4.0.1-dev-00040 Inherit CPM - - - - - - -
SimplerSoftware.EntityFrameworkCore.SqlServer.NodaTime 8.0.1 - - Inherit CPM - - - - -
Swashbuckle.AspNetCore.Annotations 6.5.0 Inherit CPM - - - - - - -

Software Design

Clean Architecture

Diagram

block-beta
	columns 4
	
	presentation["<span style='font-size: 1.6em'><strong>Presentation</strong></span><br>Pages/Views,<br>Endpoints / Controllers<br>ViewModels, API Models, Filters,<br>Model Binders, Tag Helpers<br><br><span style='font-size: 1.6em'><strong>Composition Root</strong></span><br><br><em>Other Services,<br>Inversion Dependency Interfaces</em>"]
	infra["<span style='font-size: 1.6em'><strong>Infrastructure</strong></span><br>Repositories, DbContexts, API Clients<br>File System Accessors,<br>Cloud Storage Accessors,<br>Email Implem., SMS Implem.,<br>System Clock,<br><br><em> Other Services,<br>Inversion Dependency Interfaces</em>"]
	right1<["&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"]>(right)
	db[("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style='font-size: 1.6em'> DB </span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;")]

	down2<["&nbsp;"]>(down) down3<["&nbsp;"]>(down) space space

	block:core:2
	  columns 1
	  usecases["<span style='font-size: 1.6em'><strong>Use Cases (Application Business Rules)</strong></span><br>CQRS Commands & Handlers,<br>Queries & Handlers,<br>DTOs, Behaviors "]
	  domain["<span style='font-size: 1.6em; color: initial;'><strong>Domain (Enterprise Business Rules)</strong></span><br>Entities & Aggregates, Value Objects,<br>Domain Events + Handlers, Specifications"]
	end

	style presentation fill:#4330b8,stroke:#392892,stroke-width:3px
	style infra fill:#388eee,stroke:#3778c9,stroke-width:3px
	style domain fill:#fdb643,stroke:#c18f44,stroke-width:3px,color:#000
	style db fill:#19b4d2,stroke:#469fb0,stroke-width:3px;,min-width:100px
Loading

Rules

  1. Model all business rules and entities in the Core project
  2. All dependencies flow inwards towards the Core project (Core project has no dependencies on any other project)
  3. Inner projects define interfaces; outer projects implement them

Core Project Contains

  • Interfaces
  • Entities, Aggregates
    • Events
    • Event Handlers
      • Good place for metrics, traces
    • Specifications
  • Value Objects
  • Domain Services
    • An outlier
    • Ex: Orchestration between aggregates
  • Domain "Exceptions" (not necessarily implemented as a runtime exception)

Developer Quick Start

  1. Set environment variable in your VS/VSCode launch profiles, powershell/bash/zsh user profile, user scope, or system scope:

    • NETCORE_ENVIRONMENT=Development

    Failure to do so will result in the app running in Production mode, which will not display detailed error messages.

  2. Close and re-open your terminal or VS/VSCode to ensure the environment variable is set.

    Failure to do so will result in the app running in Production mode, which will not display detailed error messages.

  3. Bootstrap docker

    cd ./WebApi/
    docker build -t dayspapet_web_api_api .

Lessons Learned the Hard Way

  1. Be careful setting your MAUI project's AndroidManifest.xml ApplicationId value. Visual Studio's manifest editor won't warn you like Android Studio does about Java reserved keywords that should never be included as individual words. Doing so results in a compile-time error like:

    1>obj\Debug\net8.0-android\android\src\com\companyname\helloworld\native\something\mymaui\R.java(8,30): javac.exe error JAVAC0000:  error: <identifier> expected
    1>obj\Debug\net8.0-android\android\src\com\companyname\helloworld\native\something\mymaui\R.java(8,30): javac.exe error JAVAC0000: package com.companyname.something.native.something.mymaui;
    1>obj\Debug\net8.0-android\android\src\com\companyname\helloworld\native\something\mymaui\R.java(8,30): javac.exe error JAVAC0000: 1 error
    1>obj\Debug\net8.0-android\android\src\com\companyname\helloworld\native\something\mymaui\R.java(8,30): javac.exe error JAVAC0000:
    

    In the example above, the word native is a reserved keyword in Java, so it should not be used as a standalone word in the ApplicationId value. For this reason, I recommend using a hyphenated or underscored value like com.companyname.helloworld.native-something.mymaui or com.companyname.helloworld.native_something.mymaui or just combine words as I have with nativeclient.

    ref: https://developer.android.com/studio/build/application-id ref: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html ref: dotnet/android#7489

  2. Entity Framework requires a default parameterless constructor, or a paramaterized constructor where its parameter names and types matches those of the mapped properties.

    ref: https://learn.microsoft.com/en-us/ef/core/modeling/constructors#binding-to-mapped-properties

  3. Currently, Entity Framework supports Value Objects (aka Complex Types) but this support excludes data seeding (EF's ModelBuilder.HasData).

  4. Nuget offers Central Package Management, a feature where you can manage all your projects' Nuget packages from a single solution-level Directory.Package.props file. This is useful for ensuring all projects use the same versions of packages. However, this feature currently is not straightforward when a MAUI project is in-scope of the props file:

About

License:MIT License


Languages

Language:C# 77.7%Language:HTML 8.7%Language:CSS 8.2%Language:PowerShell 3.4%Language:Dockerfile 1.1%Language:Batchfile 0.5%Language:JavaScript 0.4%