Skip to content

rdeneau/shopfoo

Repository files navigation

Shopfoo

GitHub Release

Shopfoo is a full-stack web app. It is a demo project showcasing the Safe Clean Architecture—a term I coined.

😉 The name Shopfoo is a reference to the chop suey dish and the song by System of a Down.

Technical stack

The solution is based on the SAFEr.Template. It's written in F# on both Client and Server sides:

  • Client:
  • Server:
  • Client-Server:
    • Fable.Remoting supporting the "Remoting API", with endpoints grouped between Home and Product
    • Shared ApiError type, hiding the Error domain type
    • Custom helpers for the calls to the Remoting API:
      • Types: ApiResult<'a> = Result<'a, ApiError>, ApiCall<'a> = Start | Done of ApiResult<'a>
      • Objects: fullContext.PrepareRequest(...) : Cmdercmder.ofApiRequest(ApiRequestArgs) : Cmd<Msg>, abstracting the Elmish Cmd.OfAsync.either
    • Translations:
      • Grouped by pages, loaded on demand and cached on the Client side
      • Friendly and strongly-typed syntax for the views: e.g. translations.Home.Theme.Garden, translations.Product.Discount discount.Value

Architecture glimpses

☝️ Disclaimer: The architecture is strongly opinionated and is not a silver-bullet! Of course, you can apply it completely on your projects—as it is fairly comprehensive for code organization, although it is not production ready. You can also just pick some ideas and techniques.

The solution is fully written in F#, a multi-paradigm language. The architecture embraces both the functional programming (FP)—the core design of F#—and the object-oriented programming, in a balanced manner.

The architecture combines—or rather is inspired by—many documented architectures:

  • Clean architecture: domain-centric, multi-layer: Presentation|Infrastructure → Application → Domain
  • Hexagonal architecture: domain-centric—the hexagon contains the Application and Domain layers—the Presentation layer is at its left—it's the driving part—the Infrastructure is at its right—the driven part.
  • Modular monolith: multi-domain solution
  • Screaming architecture: the domain is expressed directly through the file/folder organization
  • Vertical slice architecture: gathering by features rather than by technical aspects
  • SAFE architecture: Client-Shared-Server 3-project solution, F# full-stack

Hence its name, ❝ Safe Clean Architecture. ❞

Run the application

Run the relevant command(s) in a console at the root of the solution:

# Both Client and Server
dotnet run

# Server only (fast: no dotnet restore)
dotnet run Server

# Client only (fast: no dotnet restore)
yarn start:fast

# Client only, with dotnet restore
yarn start

Then, you can browse the application at the URL: http://localhost:8080.

Features

Shopfoo is the back-office tool to manage a shop selling products, limited to 5 well-known technical books for simplicity's sake.

Multi-domain features

💡 Inspired by Task based UI, a YouTube video by Derek Comartin.

Several domains are involved: Catalog, Sales, Purchases, Warehouse.

  • Catalog Info
    • Index
      • Display the table of products
        • Display ellipsis for texts longer than 2 lines
      • Handle two categories: Books and Goodies
        • Filter by categories, book author
          • New component "Filter"
      • Add new product
      • Delete product (if no stock, no sales, no purchases)
    • Details
      • Edit the image, with preview.
      • Edit the product name: required, max 128 chars.
      • Edit the product description: max 512 chars.
      • Edit the book authors
        • Select/Unselect existing author(s)
          • New component "MultiSelect"
        • Add new author
  • Sales
    • List price (a.k.a. recommended price)
      • Display the price fetched from the server
      • Action: Define if not defined
      • Action: Modify if defined (intent: Increase or Decrease)
      • Action: Remove if defined
    • Retail price
      • Display the price fetched from the server
      • Display discount / list price if any
      • Display margin / Purchased price (last or average over 1Y)
      • Action: Modify (intent: Increase or Decrease)
      • Action: Mark as sold out
    • Sales
      • Display last sale
      • Display 1Y sale: quantity, total
      • Action: Input sales
  • Purchasing
    • Purchased price
      • Display last price and average over 1Y based on the purchases and stock adjustment, with an arrow indicating if it has increased ↗️ or decreased ↘️
      • Action: Receive purchased products
  • Warehouse
    • Stock
      • Display the stock based on the stock events
      • Action: Inventory adjustment

UI features

  • Navigation bar with a breadcrumb composed of clickable segments to go up any level
  • User rights:
    • Log in using a persona to demonstrate the access rights
    • Client side: on access denied → hide button or section, use readonly inputs, redirect to an authorized page: login or page not found
    • Server side: exception → not user-friendly but just to prevent hacking
  • Localization: instant switch from English to French
  • Theme: 4 light themes and 4 dark themes, with colors preview
  • Money type to handle prices with different currency (Dollars and Euros)
    • Currency symbol position in the input box: $ 22.99 vs 22.99 €
  • Form validation

TODO

  • Deploy to Azure
  • Install Iconify and FontAwesome 6 Solid icons via Glutinum
  • Use FontAwesome icons
  • Architecture tests
  • Workflow tests
  • UI tests

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages