Minimalist E-Commerce system that's quick to set up and easy to understand. Meant for developers to adapt, customize and extend.
- Features
- Dependencies
- Deploying and running the system
- System operation under Docker Compose deployments
- Development
- CI
- An Admin Portal for managing the store.
- A REST API for building user-facing frontends.
- A simple web store frontend.
- Shopping cart handling for guests and logged in users.
- Checkout as a guest and as a logged in user.
- Product catalog and categories management.
- Product and category images upload.
- Authentication and user account management.
- Handling of customers address books.
- Discounts and coupons.
- Payment processing with Authorize.NET.
- Sales tax calculation with TaxJar.
- Email delivery for order confirmation and authentication.
- Simple backend-managed HTML content for frontends.
This system depends on the following software and services:
- .NET: the system is written using the .NET framework.
- PostgreSQL is used to store system data.
- Node.js is used to run the frontend asset bundling processes.
- pnpm is used to manage dependencies and bundling scripts.
- Authorize.NET is the web service used to process payments.
- TaxJar is the web service used to calculate sales taxes.
This system is composed of three separate web applications: The Admin Portal, the Web API and the Web Store. Depending on your use case you can pick and choose which ones to run. Keeping in mind that the Web Store depends on the Web API. Out of the box, there are three main ways to get the system up and running:
- With the included Docker Compose configuration. Recommended for production.
- With Development Containers and Visual Studio Code.
- With .NET and Node.js installed locally.
If you're trying to deploy this project somewhere, or you're just trying to run it locally without a full development environment ready, you can make use of the Docker Compose file compose.yml
found in the root directory.
In order to run the system with this approach, you'll need to have installed:
This is the approach that we recommend for production deployments.
Here's a step by step guide on how to get the system running using this deployment strategy.
Let's first go over what you'd have to do to create a new deployment from scratch.
The easiest way to get started is just to clone this repository to wherever you intend to run it from:
git clone [email protected]:EndPointCorp/end-point-commerce.git
There are several settings that need to be configured via Docker secret files and environment variables. The secrets are stored found under /secrets
and need to be modified like the following:
- Set
postgres-db-password.txt
to a secure password that will be used for the Postgres database server'spostgres
user. - Set
end-point-commerce-db-password.txt
to a secure password for the user that will be used by the EndPointCommerce services to connect to the Postgres database. - Update the connection string found in
end-point-commerce-db-connection-string.txt
to ensure it is correct for your environment. In the majority of cases, all you will need to do is simply copy the password from the previousend-point-commerce-db-password.txt
file, at put it at the end of the connection string here.
The environment variables are expected to be configured via the /.env
file. Copy /env.template
to a new /.env
file and modify it as appropriate for your deployment. /env.template
includes some default values, and these should work out of the box. There are also some empty ones that you will have to provide. Here's a description of the available variables:
ADMIN_PORTAL_PORT
,WEB_API_PORT
,WEB_STORE_PORT
andDB_PORT
determine the ports in which the various services will be run.END_POINT_COMMERCE_DB
,END_POINT_COMMERCE_DB_USERNAME
configure the system's database name and the user it will use to connect to it.ASPNETCORE_ENVIRONMENT
determines the .NET runtime environment.WEB_API_URL
is the URL of the Web API. Used by the Web Store to interact with the Web API.WEB_API_ALLOWED_ORIGINS
determines which locations are allowed to call the Web API via CORS. Set this to the URL where the Web Store is running. You can add a comma separated list of any other API clients that you may have.CATEGORY_IMAGES_URL
,PRODUCT_IMAGES_URL
determines the URLs of product and category images. Used by the Web API to link to them. This will most likely be the URL where the Web API itself will be running. But it could also be any other URL that you have configured in your web server to expose the files.WEB_STORE_CONFIRM_EMAIL_URL
,WEB_STORE_PASSWORD_RESET_URL
. These are the pages in the Web Store that support basic account management features like email confirmation and password reset.WEB_STORE_ORDER_DETAILS_URL
links to the order confirmation page. This is included in the order confirmation emails sent by the Web API.- The
MAIL_SETTINGS_*
ones determine the SMTP server to connect to for email delivery. MAIL_CC_ADDRESSES
andMAIL_BCC_ADDRESSES
determine which addresses will be sent CCs and BCCs of the emails generated by the system.- The
TAX_JAR_*
ones determine the TaxJar credentials to use for sales tax calculations. - The
AUTH_NET_*
ones determine the Authorize.NET credentials to use for payment processing.
To start up all the services and build the images run:
docker compose up -d --build
This will start them all up in the background. You can follow the logs live while everything starts up by running:
docker compose logs -f
Please note that with the way the
compose.yml
is out-of-the-box in this repository, the images that will be run are configured to be built locally from the variousDockerfile
s that are also found in the root of this repository. The images you end up running will be directly built from the current commit of this repository that you have cloned. Most "real world" production deployments would likely wish to use tagged images built and pushed elsewhere (such as via a CI/CD pipeline). To do this, you will need to make some simple modifications to thecompose.yml
.
Upon first deployment, a PostgreSQL database will be up and running in its own container. However, this database is empty and needs to be initialized. In order to do that, the EF Core database migrations need to run. The Docker Compose file provides a maintenance
container as a convenience which can be used to do various tasks, including applying database migrations.
To run the migrations simply run:
docker compose exec maintenance run-migrations.sh
After a short wait, they should complete successfully.
If this is the first time you've run the migrations, you will likely see the following error:
Failed executing DbCommand (16ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT migration_id, product_version
FROM "__EFMigrationsHistory"
ORDER BY migration_id;
As long as you see one or more Applying migration ...
lines after this error, you can safely ignore it for the first run, as it is expected (the table in question doesn't exist until the migrations have been run at least once). It shouldn't happen again on subsequent runs.
Assuming you made no changes to the .env
variables regarding ports, you should now be able to access the running services via the following URLs:
- AdminPortal: http://localhost:8001
- WebApi: http://localhost:8002/swagger
- WebStore: http://localhost:8003
And the database will be running on port 5432. Which you can connect to using:
psql -h localhost -d end_point_commerce -U end_point_commerce -W
The password being defined in the secrets/end-point-commerce-db-password.txt
file.
If you want to expose the applications to the internet via an NGINX reverse proxy, consider the included nginx.conf
as an example.
In order to log into the Admin Portal, you will need to create an admin user account first. This can be done from the maintenance
container with the EndPointCommerce.Jobs
project. You can follow these steps to create the user account:
- Connect to the maintenance container with
docker compose exec maintenance bash
. - Load the database connection string in the env var where the
Jobs
project expects it:export ConnectionStrings__EndPointCommerceDbContext=$(cat /run/secrets/end-point-commerce-db-connection-string)
. - Change into the
EndPointCommerce.Jobs
directory withcd EndPointCommerce.Jobs
. - Run the
create_admin_user
command with:dotnet run -- create_admin_user -u USERNAME -e EMAIL -p PASSWORD
. Choosing your desiredUSERNAME
,EMAIL
andPASSWORD
.
Updating an existing deployment with an updated version more or less follows the steps outlined above, assuming you haven't made significant modifications to the compose.yml
to suit your particular environment.
Stop everything that is running currently:
docker compose down
Either run git pull
or re-copy your updates to your deployment environment however you prefer in your particular environment.
Now start everything back up again and re-build updated images (unless you have updated your compose.yml
to pull tagged images from a repository, then you can of course omit the --build
argument)
docker compose up -d --build
If your update includes any database migrations, you should run them now:
docker compose exec maintenance run-migrations.sh
You can also check if there are any pending database migrations that need to be run first if you prefer:
docker compose exec maintenance check-migrations.sh
From time to time, clean up old dangling images with
docker image prune
.
This solution has Development Containers configuration in place. This means that a containerized development environment can be spun up and used to develop and test the system. The relevant files are located under the .devcontainer
directory.
In order to run a Development Container, you need:
Here's a list of steps to get this running:
Once you have all the above installed, clone this repo:
git clone [email protected]:EndPointCorp/end-point-commerce.git
You will then also have to configure some of the system settings via the appsettings.Development.json
files. Most of them, not all, are already preset to values that make sense for a development environment using Development Containers. Here are the ones that are missing:
-
In
EndPointCommerce.WebApi/appsettings.Development.json
, set:- The settings under
MailSettings
to those of your SMTP server. - The
TaxJar*
settings to those of your TaxJar account. - The
AuthNet*
settings to those of your Authorize.NET account.
- The settings under
-
In
EndPointCommerce.WebStore/appsettings.Development.json
, set:- The
AuthNet*
settings to those of your Authorize.NET account.
- The
You should configure these to have all the features working.
Finally, open the project directory in VS Code and run the "Dev Containers: Reopen in Container" command via the Command Palette (Ctrl+Shift+P).
That will download and build the necessary images and containers: one for the PostgreSQL database and one with .NET for running and debugging the apps themselves. Once done, VS Code will connect to the app container and you'll be able to develop in the container as if it was your host machine. Node.js
, along with pnpm
, as well as the dotnet-aspnet-codegenerator
and dotnet-ef
tools will also be installed without needing any extra steps.
On first run, it'll be necessary to create the database and apply all migrations with dotnet ef database update --project EndPointCommerce.Infrastructure --startup-project EndPointCommerce.AdminPortal
. This will work if run from within the root directory of this repository.
You can now run the apps and the tests. You can run tests with dotnet test
and they should all pass now.
You can start any of the apps by running dotnet run
from within their respective directories: ./EndPointCommerce.AdminPortal
for the Admin Portal, ./EndPointCommerce.WebApi
for the Web API, and ./EndPointCommerce.WebStore
for the Web Store. You can access the running apps at:
- The Admin Portal at https://localhost:7025
- The Web API at https://localhost:7043/swagger
- The Web Store at https://localhost:7106
Note also that you may need to trust the ASP.NET Core developer certificate in order to be able to run the apps using HTTPS, like they are set up to do by default. Check more details on that here: https://aka.ms/aspnet/https-trust-dev-cert.
In order to log into the Admin Portal, you will need to create an admin user account first. This can be done with the EndPointCommerce.Jobs
project. You can follow these steps to create the user account:
- Change into the
EndPointCommerce.Jobs
directory withcd EndPointCommerce.Jobs
. - Run the
create_admin_user
command with:dotnet run -- create_admin_user -u USERNAME -e EMAIL -p PASSWORD
. Choosing your desiredUSERNAME
,EMAIL
andPASSWORD
.
Like any .NET application, this system can run in any machine with the necessary software installed. In order to run the system with this method, you'll need:
Here's a step by step guide on how to get the system running using this deployment strategy.
The process of installing this software will vary depending on your environment. But here's what that could look like in Ubuntu 24.04 and similar Linux distros.
To install .NET:
- Run
sudo add-apt-repository ppa:dotnet/backports
to install the package repository where supported .NET versions are available. - Run
sudo apt-get update && sudo apt-get install dotnet-sdk-9.0
to install the .NET 9.0 SDK. - Run
dotnet --version
to validate the installation succeeded.
To install .NET CLI tools:
- Run
dotnet tool install dotnet-ef --global
. - Run
dotnet tool install dotnet-aspnet-codegenerator --global
.
To install Node.js and PNPM:
- Run
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo bash -
to install the package repository for Node.js. - Run
sudo apt-get update && sudo apt-get install nodejs
to install Node.js. - Run
sudo npm install -g pnpm@10
to install PNPM. - Run
node --version
andpnpm --version
to validate the installation succeeded.
Like mentioned before, the system needs a PostgreSQL database. There are many ways to set one up, either locally, in the same machine where the system is running, or in some external server. Using Docker though, it's possible to get a PostgreSQL database up and running quickly:
docker run -d \
--name end-point-commerce-postgres \
-p 5432:5432 \
-e POSTGRES_DB=end_point_commerce \
-e POSTGRES_USER=end_point_commerce \
-e POSTGRES_PASSWORD=password \
postgres:17.2-bookworm
It would also help to install the psql CLI client to be able to connect to this database. That can be done with:
sudo apt-get update && sudo apt-get install postgresql-client
Now you can connect to the database using:
psql -h localhost -d end_point_commerce -U end_point_commerce -W
When psql asks for a password, just type in password
. That's what the POSTGRES_PASSWORD
env var was set to, in the docker run
command.
Now that all the necessary system software is in place, you can run the applications. First clone this repository, anywhere you like:
git clone [email protected]:EndPointCorp/end-point-commerce.git
Now you need to update several settings in the various appsettings.*.json
files to have a fully working system. Some of them are already preset to values that should work out of the box. But there are also some that you will have to configure yourself, as they will depend on you particular environment.
A note on
appsettings
files: In this code base, you can configure settings using the default mechanisms supported by the .NET framework. That means you can use either of the includedappsettings.json
andappsettings.Development.json
files. In addition to that, for an added convenience during local development,appsettings.Local.json
files are also supported.They are completely optional. If present, these will be evaluated at the highest-level precedence during settings files evaluation. This means that any settings you put in a
appsettings.Local.json
file will override equivalent settings found in any of the other files. This is useful to let you easily override any per-environment settings like file paths, database connection settings, URLs, etc. that are unique to your system.Additionally, the
appsettings.Local.json
files are listed in.gitignore
, so you aren't forced to modifyappsettings.json
and then have to occasionally deal with Git conflicts when pulling other changes that might have modified these same files.So, in the following section, we explain how to set certain settings. You can choose whichever
appsettings.*.json
you like.
The most important appsettings.*.json
changes, which are required to start up the apps, are these:
-
In
EndPointCommerce.AdminPortal/appsettings.*.json
, set:ConnectionStrings.EndPointCommerceDbContext
toHost=localhost;Database=end_point_commerce;Username=end_point_commerce;Password=password
.CategoryImagesPath
toREPO_ROOT/images/category-images
.ProductImagesPath
toREPO_ROOT/images/product-images
.AdminPortalDataProtectionKeysPath
toREPO_ROOT/data-protection-keys/admin-portal
.
-
In
EndPointCommerce.WebApi/appsettings.*.json
, set:ConnectionStrings.EndPointCommerceDbContext
toHost=localhost;Database=end_point_commerce;Username=end_point_commerce;Password=password
.CategoryImagesPath
toREPO_ROOT/images/category-images
.ProductImagesPath
toREPO_ROOT/images/product-images
.WebApiDataProtectionKeysPath
toREPO_ROOT/data-protection-keys/web-api
.
-
In
EndPointCommerce.Jobs/appsettings.*.json
, set:ConnectionStrings.EndPointCommerceDbContext
toHost=localhost;Database=end_point_commerce;Username=end_point_commerce;Password=password
.
-
In
EndPointCommerce.Tests/appsettings.*.json
set:ConnectionStrings.EndPointCommerceDbContext
toHost=localhost;Database=end_point_commerce_test;Username=end_point_commerce;Password=password
. Notice thatDatabase
is set toend_point_commerce_test
in this connection string. This is the database used for integration testing.
And here's the rest of them, which enable other secondary functionality like emails and interactions with TaxJar and Authorize.NET:
-
In
EndPointCommerce.WebApi/appsettings.*.json
, set:- The settings under
MailSettings
to those of your SMTP server. - The
TaxJar*
settings to those of your TaxJar account. - The
AuthNet*
settings to those of your Authorize.NET account. MailCcAddresses
to any emails you want to receive CC of emails generated by the system.MailBccAddresses
to any emails you want to receive BCC of emails generated by the system.
- The settings under
-
In
EndPointCommerce.WebStore/wwwroot/appsettings.*.json
, set:- The
AuthNet*
settings to those of your Authorize.NET account.
- The
Considering
REPO_ROOT
as the root directory of this repository. This should be an absolute path.
With the app settings configured, we're able to initialize the database by running the migrations. This can be done with:
dotnet ef database update --startup-project ./EndPointCommerce.AdminPortal --project ./EndPointCommerce.Infrastructure
You can now run the apps and the tests. You can run tests with dotnet test
and they should all pass now.
You can now start any of the apps by running dotnet run
from within their respective directories: ./EndPointCommerce.AdminPortal
for the Admin Portal, ./EndPointCommerce.WebApi
for the Web API, and ./EndPointCommerce.WebStore
for the Web Store. You can access the running apps at:
- The Admin Portal at https://localhost:7025
- The Web API at https://localhost:7043/swagger
- The Web Store at https://localhost:7106
Note also that you may need to trust the ASP.NET Core developer certificate in order to be able to run the apps using HTTPS, like they are set up to do by default. Check more details on that here: https://aka.ms/aspnet/https-trust-dev-cert.
In order to log into the Admin Portal, you will need to create an admin user account first. This can be done with the EndPointCommerce.Jobs
project. You can follow these steps to create the user account:
- Change into the
EndPointCommerce.Jobs
directory withcd EndPointCommerce.Jobs
. - Run the
create_admin_user
command with:dotnet run -- create_admin_user -u USERNAME -e EMAIL -p PASSWORD
. Choosing your desiredUSERNAME
,EMAIL
andPASSWORD
.
This is a quick guide on how to deploy changes to an already running system that was deployed using the include Docker Compose configuration:
- Checkout the desired commit or otherwise apply desired code changes.
- If needed, review and/or update the
.env
file and the files inside thesecrets
directory. - If you want to shut down the system, you can stop the containers with
docker compose stop
. - If you want to monitor the system while deploying, tail the logs with
docker compose logs -f
. - Rebuild and restart the system with
docker compose up -d --build
. - If needed, check the pending migrations with
docker compose exec maintenance check-migrations.sh
. - If needed, run the pending migrations with
docker compose exec maintenance run-migrations.sh
. - If needed, create a new Admin Portal user account with
docker compose exec maintenance bash
,export ConnectionStrings__EndPointCommerceDbContext=$(cat /run/secrets/end-point-commerce-db-connection-string)
,cd EndPointCommerce.Jobs
anddotnet run -- create_admin_user -u USERNAME -e EMAIL -p PASSWORD
. - From time to time, cleanup old dangling images with
docker image prune
.
docker compose exec maintenance bash
opens a bash session on the maintenance container.docker compose stop
stops all containers.docker compose down
deletes all containers.docker compose logs -f
tails the logs of all containers.docker compose logs -f admin-portal
tails the logs of the Admin Portal.docker compose logs -f web-api
tails the logs of the Web API.docker compose logs -f db
tails the logs of the database.docker image ls -a
lists existing Docker images.docker image prune
deletes dangling images.docker builder prune
deletes unused Docker builder cache.
The database process is running in its own container. The data files will be stored in a Docker Volume named db-postgres-data
.
You can connect to the database using:
psql -h localhost -d end_point_commerce -U end_point_commerce -W
For this to work, you will need to have the psql
CLI client installed.
You can also connect to the database from within the maintenance
container. To do so, first connect to the container:
docker compose exec maintenance bash
And then:
psql -h db -d end_point_commerce -U end_point_commerce -W
The password can be found in
secrets/end-point-commerce-db-password.txt
.
In addition to the previously mentioned methods for connecting to the database as a client, using psql
; we can also access it by connecting directly to the db
container, where the postgres
process is running. This can be useful for performing database administration tasks, where one would need to interact directly with the postgres
process (e.g. to stop it and reboot it) and/or its configuration files.
From within the root of this directory, you can use this to connect to the db
container:
docker compose exec db bash
Within this container, you'll find a full environment and file system where the postgres
process runs. You will be connected as root
, so a simple psql
command can be used to connect to the database:
root@fa67dd17d72e:/# psql
psql (15.6 (Debian 15.6-1.pgdg120+2))
Type "help" for help.
postgres=#
As root, you'll be connected to the
postgres
database by default. The application's database isend_point_commerce
.
This container is based on the official postgres
image from Dockerhub. That means that the environment inside this container is going to look like any default postgres
installation. As such, the data directory is found in the default location /var/lib/postgresql/data
:
root@fa67dd17d72e:/# ls /var/lib/postgresql/data
base pg_dynshmem pg_logical pg_replslot pg_stat pg_tblspc pg_wal postgresql.conf
global pg_hba.conf pg_multixact pg_serial pg_stat_tmp pg_twophase pg_xact postmaster.opts
pg_commit_ts pg_ident.conf pg_notify pg_snapshots pg_subtrans PG_VERSION postgresql.auto.conf postmaster.pid
This directory is backed by a Docker Volume. Which means that any changes made here will persist beyond the lifetime of the db
container. The container itself is stateless, and all it does is run the postgres
process. It can be destroyed and rebooted and the data in this directory will stay the same.
As a final note, always keep in mind that this is a containerized deployment which is managed by Docker configuration files. This means that, to the best of our abilities, the full system configuration needs to be reflected by these files. And documented explicitly in this README if for whatever reason this is not possible.
So, for any configuration change that is done to the database in an ad-hoc manner using the approach described in this section, we need to make sure to update the corresponding config files in this repo. For the database, the changes would likely be made on one or more of these files: Dockerfile.DB
, compose.yaml
, and deploy-utils/db/initialize.sh
.
When run via Docker using the included compose.yaml
, the AdminPortal, WebApi and Maintenance containers all mount the same images
Docker volume which stores both category and product images.
During local development, this repo contains the directories images/category-images
and images/product-images
which are meant to store category and product image files. The Admin Portal and Web API will expect these directories to exist.
Our system uses cookies which are protected using .NET's Data Protection API. To make sure it works well across deployments, part of its configuration involves storing the keys in a non-volatile location.
When run via Docker using the included compose.yaml
, these keys are stored in individual volumes that are unique to each container (admin-portal-data-protection-keys
, web-api-data-protection-keys
).
Otherwise, during local development the data-protection-keys
directory included in this repo serves that purpose. The Admin Portal, Web API and Web Store containers use it to store their respective keys.
More info here:
- The key was not found in the key ring. Unable to validate token
- Configure ASP.NET Core Data Protection - PersistKeysToFileSystem
- Configure ASP.NET Core Data Protection - Persisting keys when hosting in a Docker container
During normal operations of building and rebuilding images and containers, the docker engine will accumulate old, untagged, unreferenced images and old builder cache on disk.
To see all the existing images, use docker image ls -a
. The ones which have <none>
in their TAG and REPOSITORY are the dangling ones.
From time to time, it's good to clean these up. This can be done with docker image prune
.
The unused builder cache can also be cleaned with docker builder prune
.
dotnet build
builds all the projects in the solution. Can be run within the directory of any of the projects to build that particular project.dotnet test
runs the automated tests.dotnet test --logger "console;verbosity=normal"
produces a more verbose output.dotnet run
runs a particular app. Needs to be run from one of these directories:./EndPointCommerce.AdminPortal
,./EndPointCommerce.WebApi
,./EndPointCommerce.WebStore
.pnpm install
downloads JavaScript frotnend packages. Needs to be run from one of these directories:./EndPointCommerce.AdminPortal
,./EndPointCommerce.WebStore
.pnpm build
bundles frontend assets. Needs to be run from one of these directories:./EndPointCommerce.AdminPortal
,./EndPointCommerce.WebStore
.dotnet ef migrations has-pending-model-changes -s EndPointCommerce.AdminPortal -p EndPointCommerce.Infrastructure
checks if there are model changes that would warrant a new database migration.dotnet ef migrations add [MIGRATION_NAME] -s EndPointCommerce.AdminPortal -p EndPointCommerce.Infrastructure
creates new database migrations.dotnet ef migrations list -s EndPointCommerce.AdminPortal -p EndPointCommerce.Infrastructure
lists existing migrations and distinguishes between applied and pending ones.dotnet ef migrations remove -s EndPointCommerce.AdminPortal -p EndPointCommerce.Infrastructure
removes the latest migration.dotnet ef database update -s EndPointCommerce.AdminPortal -p EndPointCommerce.Infrastructure
applies pending migrations.dotnet ef database update [MIGRATION_NAME] -s EndPointCommerce.AdminPortal -p EndPointCommerce.Infrastructure
applies or rolls back migrations to bring the database state to the given migration.
All tests can be run with dotnet test
, from the root of this repo. For more verbose output, you can use dotnet test -l "console;verbosity=normal"
.
The automated tests can be found in the EndPointCommerce.Tests
project. There are both unit and integration tests in there. Unit tests exercise specific classes and methods in isolation or with mocked dependencies. Integration tests have a wider scope and exercise interactions with external components, like the database, and end-to-end tests that simulate HTTP requests.
This solution includes two ASP.NET Core applications with frontends: EndPointCommerce.AdminPortal
and EndPointCommerce.WebStore
. These applications use SCSS and JavaScript for frontend styling and functionality. The JavaScript files are stored in each project's JavaScript
directory, while the SCSS files are stored in each project's Stylesheets
directory.
These files depend on third party libraries and need to be bundled in order to be usable in the browser. So, for managing and bundling frontend assets, we use the PNPM package manager and the esbuild bundler. This is apparent by the presence of package.json
, pnpm-lock.yaml
and esbuild.config.mjs
files in those two projects' root directories.
The esbuild
bundling process is configured to ingest the JavaScript and SCSS files from the JavaScript
and Stylesheets
directories and produce bundled versions of them into the projects' wwwroot
directory. Source maps are also included. Feel free to review both esbuild.config.mjs
files to learn more.
There are no additional steps necessary to bundle the frontend assets. That's because both projects have been configured to trigger the frontent asset bundling process as part of the usual dotnet build
command. This was done by including the following in both projects' *.csproj
files:
<Target Name="BundleFrontendAssets" BeforeTargets="Build">
<Exec Command="pnpm install" />
<Exec Command="pnpm run build" />
</Target>
Of course, in case you want to manually run these or any other Node.js commands, you can do so from the projects' root directories. That is:
cd EndPointCommerce.AdminPortal
# or
cd EndPointCommerce.WebStore
# and then...
pnpm install
pnpm run build
This code base is a .NET solution organized in a simple structure inspired by Clean Architecture and Domain Driven Design.
The projects are:
- EndPointCommerce.WebApi: An ASP.NET Web API project that contains a REST API meant to be consumed by a frontend, user-facing store application. Exposes the core functionality needed for an E-Commerce site.
- EndPointCommerce.AdminPortal: An ASP.NET Razor Pages Web app project meant for site administrators to manage various aspects of the store.
- EndPointCommerce.WebStore: A Blazor WebAssembly Standalone app project which contains a simple web store frontend. Demonstrates the functionality that's available in the Web API and how to implement payment processing with Authorize.NET.
- EndPointCommerce.Jobs: A .NET Console Application project which contains backend jobs that can be run on demand. The included
create_admin_user
command creates user accounts that can log into the Admin Portal. - EndPointCommerce.Domain: A class library project that contains the business logic.
- EndPointCommerce.Infrastructure: A class library project that contains components for interacting with database and other system software as well as third party software and (web) APIs.
- EndPointCommerce.RazorTemplates: A class library project that contains utilities for rendering text content using the Razor rendering engine. The system uses this to render HTML emails.
- EndPointCommerce.Tests: An xUnit project that contains tests of varying granularity for all the projects in the solution. Every project has its own directory where their respective tests live.
Here's a diagram that highlights the main architectural components of the system and their basic interactions.
In the top most layer we have the executable applications: The Web API, the Admin Portal and the Web Store.
The Web API and the Admin Portal are regular ASP.NET Core web applications. The Web API uses MVC while the Admin Portal uses Razor Pages. In the Web API, there are a number of Controllers which define all the endpoints that are available. There is also a series of Resource Models that represent the data structures for the request and response payloads of those endpoints.
The Admin Portal, being a Razor Pages project, defines Pages which implement all the screens in the application, both the view templates and the code behind them. It also defines a series of View Models that allow passing data to and from the Razor view templates for rendering purposes and capturing user actions.
Both the Web API and Admin Portal depend on the Domain and Infrastructure layers in order to expose their functionality to clients and users.
The Web Store is an Blazor WebAssembly Standalone app which offers a user facing frontend. The only way in which it integrates with the rest of the system is by interacting with the Web API via HTTP.
Next we have the Domain layer, implemented as a .NET class library project. This is the core of the system. It contains the logic that fulfills the business requirements. This is done through: 1. Entities that represent the main concepts of the domain (and closely resemble the tables in the database); and 2. domain Services that implement the use cases. Under EndPointCommerce.Domain/Services
, you'll find a number of service objects, each one of which implements one system operation or use case. Tasks like "add an item to a cart" or "place and order" are implemented here, each in their own separate class.
The domain project stands on its own. It does not depend on any other project. Instead, other projects depend on it. It does need objects from the Infrastructure layer, but instead of depending directly on its classes, it just defines the interfaces the they need to implement and references those. Dependency Injection then takes care of creating and providing the concrete instances that meet the interface requirements put forth by the Domain layer.
Finally the infrastructure layer contains all the logic for interacting with services that are external to the system. It provides classes for working with the file system, the database (via Repositories), STMP servers, Authorize.NET (the payment gateway) and TaxJar (the tax calculation web service). All the things that are required (and specified, via interfaces) by the Domain layer. Same as Domain, it is a .NET class library project.
As a special case, there is the Razor Templates component which is used by the Infrastructure project's email delivery services to render email content. Due to technical details this is implemented as a separate .NET class library project, but conceptually it is part of the Infrastructure layer.
Following is an overview of the data model, including the most notable tables and fields. This also maps generally pretty cleanly to the main domain entities implemented in this system.
-
quotes
andquote_items
: These tables store shopping carts and the items within them. Thequotes.is_open
flag indicates whether a cart is open or closed. A cart is closed when an order for it is placed. -
orders
andorder_items
: These tables store orders and their items. Orders contain a copy of all the information from the quote that produced them. They also contain the pricing of all items at the time of purchase. That is, the amount that the customer got charged. Theorders.payment_*
store Authorize.NET transaction identifiers and tokens. -
products
andcategories
: These tables contain the catalog of available products and the categories that they belong to. Both tables include a series ofmeta_
fields that are meant to populate<meta>
tags in product pages in web frontends. These help with SEO purposes.url_key
is a unique field that can be used to build product page URLs. There is also an API endpoint available that allows fetching products using this field.products.search_vector
is an auto-calculated field that enables PostgreSQL's full text search functionality on products. They also support associated images, which are stored in the various*_image_id
fields. -
customers
andusers
: These tables store customer accounts and users with login capability. When a guest places an order, acustomers
record is created for it, without ausers
record. Customers that sign up to the system will also have ausers
record, which allows them to log in. -
coupons
: This table stores available coupon codes. Coupons apply discounts to quotes and orders. The discount can be a fixed amount or based on a percentage of the total value of the shopping cart. -
addresses
: This table stores billing and shipping addresses associated with quotes, orders and customers.