• This repository has been archived on 23/Mar/2024
  • Stars
    star
    124
  • Rank 288,207 (Top 6 %)
  • Language
    Go
  • License
    MIT License
  • Created over 4 years ago
  • Updated 8 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Sample application showcasing the use of Dapr to build microservices based apps

Dapr Store

Dapr Store is a sample/reference application showcasing the use of Dapr to build microservices based applications. It is a simple online store with all the core components that make up such system, e.g. a frontend for users, authentication, product catalog, and order processing etc.

Dapr is an "event-driven, portable runtime for building microservices on cloud and edge". The intention of this project was to show off many of the capabilities and features of Dapr, but in the context of a real working application. This has influenced the architecture and design decisions, balancing between realism and a simple "demo-ware" showcase.

The backend microservices are written in Go (however it's worth nothing that Dapr is language independent), and the frontend is a single-page application (SPA) written in Vue.js. All APIs are REST & HTTP based

This repo is a monorepo, containing the source for several discreet but closely linked codebases, one for each component of the project, as described below.
The "Go Standard Project Layout" has been used.

Architecture

The following diagram shows all the components of the application and main interactions. It also highlights which Dapr API/feature (aka Dapr building block) is used and where. architecture diagram

Dapr Interfaces & Building Blocks

The application uses the following Dapr Building Blocks and APIs

  • Service Invocation — The API gateway calls the four main microservices using HTTP calls to Dapr service invocation. This provides retries, mTLS and service discovery.
  • State — State is held for users and orders using the Dapr state management API. The state provider used is Redis, however any other provider could be plugged in without any application code changes.
  • Pub/Sub — The submission of new orders through the cart service, is decoupled from the order processing via pub/sub messaging and the Dapr pub/sub messaging API. New orders are placed on a topic as messages, to be collected by the orders service. This allows the orders service to independently scale and separates our reads & writes
  • Output Bindings — To communicate with downstream & 3rd party systems, the Dapr Bindings API is used. This allows the system to carry out tasks such as saving order details into external storage (e.g. Azure Blob) and notify uses with emails via SendGrid
  • Middleware — Dapr supports a range of HTTP middleware, for this project traffic rate limiting can enabled on any of the APIs with a single Kubernetes annotation

Project Status

Deployed instance: https://daprstore.kube.benco.io/

Application Elements & Services

The main elements and microservices that make up the Dapr Store system are described here

Each service uses the Go REST API Starter Kit & Library as a starting basis, lots of the boilerplate and repeated code is located there.

Service Code

Each Go microservice (in cmd/) follows a very similar layout (the exception being frontend-host which has no business logic)

Primary runtime code:

  • main.go - Starts HTTP server, creates service implementation + main entry point
  • routes.go - All controllers for routes exposed by the service's API
  • spec/spec.go - Specification of domain entity (e.g. User) and interface to support it
  • impl/impl.go - Concrete implementation of the above spec, backed by either Dapr or other dependency (e.g. a database)

For testing:

  • *_test.go - Main service tests
  • mock/mock.go - Mock implementation of the domain spec, with no dependencies

💰 Orders service

This service provides order processing to the Dapr Store.
It is written in Go, source is in cmd/orders and it exposes the following API routes:

/get/{id}                GET a single order by orderID
/getForUser/{userId}   GET all orders for a given user

See cmd/orders/spec for details of the Order entity.

The service provides some fake order processing activity so that orders are moved through a number of statuses, simulating some back-office systems or inventory management. Orders are initially set to OrderReceived status, then after 30 seconds moved to OrderProcessing, then after 2 minutes moved to OrderComplete

Orders - Dapr Interaction

  • Pub/Sub. Subscribes to the orders-queue topic to receive new orders from the cart service
  • State. Stores and retrieves Order entities from the state service, keyed on OrderID. Also lists of orders per user, held as an array of OrderIDs and keyed on username
  • Bindings. All output bindings are optional, the service operates without these present
    • Azure Blob. For saving "order reports" as text files into Azure Blob storage
    • SendGrid. For sending emails to users via SendGrid

👦 Users service

This provides a simple user profile service to the Dapr Store. Only registered users can use the store to place orders etc.
It is written in Go, source is in cmd/users and it exposes the following API routes:

/register               POST a new user to register them
/get/{userId}           GET the user profile for given user
/private/get/{userId}   GET the user profile for given user. Private endpoints are NOT exposed through the gateway
/isregistered/{userId}  GET the registration status for a given user

See cmd/users/spec for details of the User entity.

The service is notable as it consists of a mix of both secured API routes, and two that are anonymous/open /isregistered and /private/get

Users - Dapr Interaction

  • State. Stores and retrieves User entities from the state service, keyed on username.

📑 Products service

This is the product catalog service for Dapr Store.
It is written in Go, source is in cmd/products and it exposes the following API routes:

/get/{id}        GET a single product with given id
/catalog         GET all products in the catalog, returns an array of products
/offers          GET all products that are on offer, returns an array of products
/search/{query}  GET search the product database, returns an array of products

See cmd/products/spec for details of the Product entity.

The products data is held in a SQLite database, this decision was taken due to the lack of support for queries and filtering with the Dapr state API. The source data to populate the DB is in etc/products.csv and the database can be created with the scripts/create-products-db.sh. The database file (sqlite.db) is currently stored inside the products container, effectively making catalogue baked in at build time. This could be changed/improved at a later date

Products - Dapr Interaction

None directly, but is called via service invocation from other services, the API gateway & the cart service.

🛒 Cart service

This provides a cart service to the Dapr Store. The currently implementation is a MVP.
It is written in Go, source is in cmd/cart and it exposes the following API routes:

/setProduct/{userId}/{productId}/{count}    PUT a number of products in the cart of given user
/get/{userId}                               GET cart for user
/submit                                       POST submit a cart, and turn it into an 'Order'
/clear/{userId}                             PUT clear a user's cart

The service is responsible for maintaining shopping carts for each user and persisting them. Submitting a cart will validate the contents and turn it into a order, which is sent to the Orders service for processing

Cart - Dapr Interaction

  • Pub/Sub. The cart pushes Order entities to the orders-queue topic to be collected by the orders service
  • State. Stores and retrieves Cart entities from the state service, keyed on username.
  • Service Invocation. Cross service call to products API to lookup and check products in the cart

💻 Frontend

This is the frontend accessed by users of store and visitors to the site. It is a single-page application (SPA) as such it runs entirely client side in the browser. It was created using the Vue CLI and written in Vue.js

It follows the standard SPA pattern of being served via static hosting (the 'frontend host' described below) and all data is fetched via a REST API endpoint. Note. Vue Router is used to provide client side routing, as such it needs to be served from a host that is configured to support it.

The default API endpoint is / and it makes calls to the Dapr invoke API, namely /v1.0/invoke/{service} this is routed via the API gateway to the various services.

📡 Frontend host

A very standard static content server using gorilla/mux. See https://github.com/gorilla/mux#static-files. It simply serves up the static bundled files output from the build process of the frontend, it expects to find these files in ./dist directory but this is configurable

In addition it exposes a simple /config endpoint, this is to allow dynamic configuration of the frontend. It passes two env vars AUTH_CLIENT_ID and API_ENDPOINT from the frontend host to the frontend Vue SPA as a JSON response, which are fetched & read as the app is loaded in the browser.

🌍 API gateway

This component is critical but consists of no code. It's a NGINX reverse proxy configured to do two things:

  • Forward specific calls to the relevant services via Dapr
  • Block requests to private APIs
  • Direct requests to the frontend host

Note. This is not to be confused with Azure API Management, Azure App Gateway or AWS API Gateway 😀

This is done with path based routing, it aggregates the various APIs and frontend SPA into a single endpoint or host, making configuration much easier (and the reason the API endpoint for the SPA can simply be /)

NGINX is run with the Dapr sidecar alongside it, so that it can proxy requests to the /v1.0/invoke Dapr API, via this the downstream services are invoked, through Dapr.

Routing logic (in order of priority):

  1. Routes that match /v1.0/invoke/.*/method/private/.* are blocked with 403
  2. Routes that match /v1.0/invoke/ are proxied to the Dapr sidecar
  3. Routes that match / are proxied to the frontend host

Within Kubernetes

Inspired by this blog post it is deployed in Kubernetes as a "Daprized NGINX ingress controller". See deploy/ingress for details on how this is done.

Locally

To provide a like for like experience with Kubernetes, and the single aggregated endpoint - the same model is used locally. NGINX is run as a Docker container exposed to the host network, and NGINX configuration applied allow to route traffic. This is then run via the dapr CLI (i.e dapr run) so that the daprd sidecar process is available to it.

See scripts/local-gateway for details on how this is done, the scripts/local-gateway/run.sh script starts the gateway which will run on port 9000

Running in Kubernetes - Quick guide

📃 SUB-SECTION: Deployment guide for Kubernetes

Running Locally - Quick guide

This is a (very) basic guide to running Dapr Store locally. Only instructions for WSL 2/Linux/MacOS are provided. It's advised to only do this if you wish to develop or debug the project.

Prereqs

  • Docker
  • Go v1.18+
  • Node.js v14+

Setup

Install and initialize Dapr

wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash
dapr init

Clone repo

git clone https://github.com/benc-uk/dapr-store/

Run all services

Run everything. Run from the project root (e.g. dapr-store directory), this will run all the services, the API gateway and the Vue frontend

make run

Access the store from http://localhost:9000/

💣 Gotcha! The Vue frontend will start and display a message "App running at" saying it is running on port 8000, do not access the frontend directly on this port, it will not function!, always go via the gateway running on port 9000

Working Locally

A makefile is provided to assist working with the project and building/running it, the list of targets is:

help                 💬 This help message :)
lint                 🔎 Lint & format, check to be run in CI, sets exit code on error
lint-fix             📝 Lint & format, fixes errors and modifies code
test                 🎯 Unit tests for services and snapshot tests for SPA frontend 
test-reports         📜 Unit tests with coverage and test reports (deprecated)
bundle               💻 Build and bundle the frontend Vue SPA
clean                🧹 Clean the project, remove modules, binaries and outputs
run                  🚀 Start & run everything locally as processes
docker-run           🐋 Run locally using containers and Docker compose
docker-build         🔨 Build all containers using Docker compose
docker-push          📤 Push all containers using Docker compose
docker-stop          🚫 Stop and remove local containers
stop                 ⛔ Stop & kill everything started locally from `make run`

CI / CD

A set of CI and CD release GitHub Actions workflows are included in .github/workflows/, automated builds are run in GitHub hosted runners

GitHub Actions

Security, Identity & Authentication

The default mode of operation for the Dapr Store is in "demo mode" where there is no identity provided configured, and no security on the APIs. This makes it simple to run and allows us to focus on the Dapr aspects of the project. In this mode a demo/dummy user account can be used to sign-in and place orders in the store.

Optionally Dapr store can be configured utilise the Microsoft identity platform (aka Azure Active Directory v2) as an identity provider, to enable real user sign-in, and securing of the APIs.

📃 SUB-SECTION: Full details on security, identity & authentication

Configuration

Environmental Variables

The services support the following environmental variables. All settings are optional with default values.

  • PORT - Port the server will listen on. See defaults below.
  • AUTH_CLIENT_ID - Used to enable integration with Azure AD for identity and authentication. Default is blank, which runs the service with no identity backend. See the security, identity & authentication docs for more details.
  • DAPR_STORE_NAME - Name of the Dapr state component to use. Default is statestore

The following vars are used only by the Cart and Orders services:

  • DAPR_ORDERS_TOPIC - Name of the Dapr pub/sub topic to use for orders. Default is orders-queue
  • DAPR_PUBSUB_NAME - Name of the Dapr pub/sub component to use for orders. Default is pubsub

The following vars are only used by the Orders service:

  • DAPR_EMAIL_NAME - Name of the Dapr SendGrid component to use for sending order emails. Default is orders-email
  • DAPR_REPORT_NAME - Name of the Dapr Azure Blob component to use for saving order reports. Default is orders-report

Frontend host config:

  • STATIC_DIR - The path to serve static content from, i.e. the bundled Vue.js SPA output. Default is ./dist
  • API_ENDPOINT - To point the frontend at a different endpoint. It's very unlikely you'll ever need to set this. Default is /

Default ports

  • 9000 - NGINX API gateway (reverse proxy)
  • 9001 - Cart service
  • 9002 - Products service
  • 9003 - Users service
  • 9004 - Order processing service
  • 8000 - Frontend host

Dapr Components

📃 SUB-SECTION: Details of the Dapr components used by the application and how to configure them.

Roadmap & known issues

See project plan on GitHub

Concepts and Terms

Clarity of terminology is sometimes important, here's a small glossary

  • Building Block - Specific Dapr term. A building block is an API level feature of Dapr, such as 'state management' or 'pub/sub' or 'secrets'
  • Component - Component is another Dapr specific term. A component is a plugin that provides implementation functionality to building blocks. As component is a generic & commonly used word, the term "Dapr component" will be used where ambiguity is possible
  • Service - The microservices, written in Go and exposing REST API, either invoked through Dapr and.or using the Dapr API for things such as state.
  • API Gateway - NGINX reverse proxy sitting in front of the services. This is not to be confused with Azure API Management, Azure App Gateway or AWS API Gateway
  • State - Dapr state API, backed with a Dapr component state provider, e.g. Redis
  • Entity - A data object, typically a JSON representation of one of the structs in the spec folder, can be client or server side

More Repositories

1

kubeview

Kubernetes cluster visualiser and graphical explorer
Vue
941
star
2

k6-reporter

Output K6 test run results as formatted & easy to read HTML reports
EJS
394
star
3

workflow-dispatch

A GitHub Action for triggering workflows, using the `workflow_dispatch` event
TypeScript
322
star
4

icon-collection

Azure & Microsoft SVG Icon Collection
HTML
249
star
5

armview-vscode

Graphically display ARM templates in VS Code with an interactive map style view
TypeScript
143
star
6

python-demoapp

Simple Python Flask web application designed for running in containers for demos
JavaScript
139
star
7

super-dungeon-delve

2D retro style dungeon game
GDScript
123
star
8

vuego-demoapp

Simple Golang and Vue.js SPA demo for deploying to Azure, Docker etc
Vue
116
star
9

smilr

Microservices reference app showcasing a range of technologies, platforms & methodologies
JavaScript
99
star
10

chatr

Chat app using Azure Web PubSub, Static Web Apps and other Azure services
JavaScript
70
star
11

nodejs-demoapp

Sample Node.js web app for deploying to Azure, Kubernetes etc
JavaScript
54
star
12

htmx-go-chat

Chat app written in Go with HTMX and SSE
Go
45
star
13

msal-graph-vue

Vue.js sample app showing use of MSAL for authentication and calling MS Graph APIs
Vue
35
star
14

dotnet6-minimal

Quick and dirty minimal .NET 6 web app
C#
28
star
15

postman-prometheus

Run Postman collections continuously and export results as Prometheus metrics
JavaScript
28
star
16

dotnet-demoapp

.NET 6 demo web app for deploying to Azure, Docker etc
C#
27
star
17

kube-workshop

A hands-on workshop intended to get people comfortable in working with Kubernetes and deploying applications
26
star
18

touchmidi

Flexible HTML5 based touch based control surface for MIDI
JavaScript
25
star
19

armview-web

Standalone web version of github.com/benc-uk/armview-vscode
EJS
22
star
20

tools-install

Setup scripts for various dev tools, utilities, SDKs and CLI stuff
Shell
20
star
21

k6-prometheus-exporter

Export k6 metrics and load test data into Prometheus
Go
20
star
22

rayscale

Microservices based, distributed ray tracer designed to run in containers
TypeScript
20
star
23

go-rest-api

Boilerplate & template starter for creating a REST based HTTP microservice in Go
Go
19
star
24

bicep-iac

My set of Azure Bicep templates and modules
Bicep
17
star
25

pikube

Building a Kubernetes cluster on the Raspberry Pi
Python
16
star
26

hcl2-parser

HCLv2 parser for JavaScript
JavaScript
16
star
27

locust-reporter

Generate HTML reports from Locust load test output
Go
16
star
28

azure-arm

My ARM template library
Shell
14
star
29

batcomputer

A working example of DevOps & operationalisation applied to Machine Learning and AI
Python
13
star
30

gofract

Mandelbrot and Julia fractals rendered in real-time using Go
Go
13
star
31

keycloak-helm

Helm chart for Keycloak
Smarty
10
star
32

terraform-mgmt-bootstrap

Bootstrap core Azure state & resources using Terraform for use with Azure DevOps
HCL
9
star
33

serverless-cosmos-lab

Azure computer vision AI lab using LogicApps, Cognitive Services, CosmosDB and Web Apps
JavaScript
9
star
34

azure-functions

!! OLD PLEASE DON'T USE !! Demo and example Azure Functions in various languages
C#
9
star
35

kube-primer

Kubernetes Technical Primer
9
star
36

pod-kicker

A Kubernetes sidecar to watch for file changes and restart deployments and pods
Go
7
star
37

dotfiles

My personal dotfiles
Shell
7
star
38

aks-samples

Samples, feature demos and examples for Azure Kubernetes Service (AKS)
Shell
6
star
39

touchmidi-old

Flexible HTML5 based touch based control surface for MIDI
JavaScript
6
star
40

java-demoapp

Simple demo Java Spring Boot web app for deploying to Azure, Docker etc
Java
6
star
41

melkors-oubliette

A 3D dungeon crawler game inspired by Dungeon Master & Eye Of The Beholder
GDScript
6
star
42

devcontainers

Dockerfile
5
star
43

generative-music

Procedural and generative music tools and things
HTML
5
star
44

msal-graph-react

React sample app showing use of MSAL for authentication and calling MS Graph APIs
JavaScript
5
star
45

dart-raytracer

A raytracer written in Dart
Dart
4
star
46

goat-bot

Microsoft Bot Framework working example & demo - Goat Bot!
JavaScript
4
star
47

face-vision-app

Azure Cognitive Face/Vision APIs - Pure HTML5/JS Client
JavaScript
4
star
48

azure-samples

General collection of samples and snippets for Azure
PowerShell
4
star
49

csa-widgets

Dashboard Widgets for HP Cloud Service Automation
JavaScript
4
star
50

serverless-cognitive

Computer vision AI lab using Azure Functions and Cognitive Services
JavaScript
4
star
51

dungeon-game-old

C#
3
star
52

simple-slides

Simple slide presenter written in HTML5 & JS
HTML
3
star
53

angular-demoapp

Demo app written in Angular 4 using Node.js and Material Components
TypeScript
3
star
54

nanomon

Lightweight network and HTTP monitoring system, designed to be run with containers & Kubernetes
Go
3
star
55

azure-devops-core-docker

End to end DevOps exercise with .NET Core, Docker, VSTS in Azure
Batchfile
3
star
56

gsots3d

Library for building & rendering 3D stuff on the web
JavaScript
3
star
57

dapr-dotnet-starter

Simple learning & getting started example for .NET and Dapr
C#
2
star
58

doom-lite

A learning exercise in writing a simple retro style FPS
JavaScript
2
star
59

project-starter

A base template to be used for any project
Makefile
2
star
60

midi-toolkit

A HTML5 & JS based static web app for working with MIDI
JavaScript
2
star
61

caster

Retro style 90s FPS with a fantasy theme, written in Go
Go
2
star
62

nanoproxy

A simple HTTP reverse proxy & Kubernetes ingress controller written in Go as a learning exercise
Go
2
star
63

go-acs-client

Client SDK for the Azure Communication Services API
Go
2
star
64

serverless-fractal-bot

Fractals done in serverless and then tweeted to the world
JavaScript
2
star
65

http-toolkit

This is a simple backend service for testing and debugging HTTP requests
Go
1
star
66

tomb-viewer

Rendering Tomb Raider levels in TypeScript and WebGL
TypeScript
1
star
67

taskr

Demonstrator for Static Web Apps with Functions and Cosmos DB
JavaScript
1
star
68

cassandra-prototype

Experimenting with Cassandra
Shell
1
star
69

food-truck

Food Truck Challenge
Go
1
star
70

hivealive

IoT on Azure Proof Of Concept
JavaScript
1
star
71

gitops-test

Shell
1
star
72

mockery

API Server Mocking in Go
Go
1
star
73

azure-iot-demo

Azure IoT Demo using IoT Hubs, Service Bus and Functions
JavaScript
1
star
74

nanotracker

Retro styled music tracker in HTML5 & modern JavaScript
JavaScript
1
star
75

cse-onboarding

CSE Tech On-Boarding FY23
1
star
76

htmx-go-todo

An example of a simple HTMX app built using Go
Go
1
star
77

skytap-ansible

Skytap module for Ansible with some example playbooks
Python
1
star
78

dapr-gateway-hack

Experiment and hack for Dapr service invocation across networks and clusters
Go
1
star
79

gh-experiment

1
star
80

fractal-bot-go

Fractals & Twitter bot done with Azure Functions and Go custom handlers
Go
1
star
81

dotfiles-kit

Build your own dotfiles repo
Shell
1
star
82

nodejs-api-starter

Starter template app for a Node.js CRUD REST API using MongoDB & Mongoose
JavaScript
1
star
83

grpc-experiment

Go
1
star
84

benc.dev

blog & personal/vanity site
HTML
1
star
85

rust-wasm-fractals

Mandelbrot & Julia fractals rendered with Rust with WASM & JS
JavaScript
1
star
86

link-shortener

Link & URL shortener
Shell
1
star