Fullstack Project: Souk Atlas, a Marketplace from A to Z
Exit

Lesson 1 of 12 · Design

The Souk Atlas project: requirements, wireframes, architecture and Git repository

1 h Node.js 22 (Debian, git, npm, python3)

Objective

Lay the foundations of the common thread project of this course: Souk Atlas, a marketplace where Moroccan artisans sell rugs, pottery, leather goods and jewelry, and where customers pay by bank transfer. At the end of the lesson, you know what the application must do, what it looks like and how its three building blocks talk to each other, and you have a clean Git repository with no secrets in it.

Key points

  • A full-stack project starts on paper: user stories, wireframes, and a list of what is out of scope.
  • Souk Atlas has three building blocks: a React front end (the browser), a Node.js/Express API (the business rules) and a PostgreSQL database (the source of truth for the data).
  • The browser never talks directly to the database: everything goes through the API, which validates and recalculates.
  • A single Git repository (a "monorepo") with api/, web/ and docs/ is more than enough for a small team.
  • Secrets (.env) never go into Git: you version a .env.example template instead.

1. What this course brings together

You learned the building blocks one by one in the other MasterLinux courses:

Building block Where did you learn it? What we do with it here
HTML, CSS, JavaScript, fetch Web course (lessons 2.x and 3.2) the user interface and the API calls
Node.js, Express Node.js course the marketplace REST API
React React course the front end: catalog, cart, seller area
SQL, PostgreSQL SQL course the schema and the queries
Docker, Compose Docker course (lessons 1.4, 2.4, 2.5, 3.3) packaging and deployment
GitHub Actions CI/CD course (lessons 1.2, 2.1, 2.2) automated tests and builds

This course does not explain these basics again: it shows how to make them work together on a real product, with the decisions a full-stack developer makes every day. When a concept has already been covered, we refer you to the relevant lesson.

2. The client and their needs

The client: a fictitious cooperative, "Souk Atlas SARL", based in Fès. It wants to help about fifty artisans from Fès, Safi, Marrakech and Essaouira sell online, without depending on a foreign platform that takes a large commission.

What we know about its users:

  • buyers are mostly in Morocco, on their phones, and many do not have a bank card that can be used online;
  • artisans are not IT specialists: their screen must be simple;
  • the cooperative receives the transfers on its account, then pays the artisans.

This leads to the most structuring decision of the project: payment is made by bank transfer. The buyer places an order and receives the cooperative's RIB (bank account details) and a reference to copy into the transfer description; an administrator then matches the transfers received and marks the orders as "paid". Card payment will come in version 2.

3. User stories

A user story describes a need from the point of view of the person who has it, in one sentence: "As a role, I do something to get a benefit". It does not say how to code it.

Role Story Priority
Buyer As a buyer, I browse the catalog by category and I search for a product by its name essential
Buyer As a buyer, I fill a cart and I see the total in dirhams, delivery included essential
Buyer As a buyer, I place an order and I receive the reference to write on my transfer essential
Buyer As a buyer, I follow the status of my order (pending, paid, shipped) essential
Seller As a seller, I add, modify and remove my products essential
Seller As a seller, I see the paid orders and I mark them as shipped essential
Admin As an administrator, I mark an order as paid when the transfer arrives essential
Buyer As a buyer, I leave a review on a product later

Writing the priority column is as important as the rest: it defines the minimum viable product, what you deliver first.

And the out of scope list, written down in black and white, avoids endless discussions: card payment, native mobile application, messaging between buyer and seller, returns management. It does not mean "never", it means "not in this version".

In the lab, the requirements document docs/BESOINS.md is written in French: the stories start with "En tant qu'acheteur…" (As a buyer…) or "En tant que vendeur…" (As a seller…).

4. Wireframes

Before coding, you draw the screens. A pencil and paper are enough, or a tool such as Figma or Penpot. Here are the five screens of version 1, as "wireframe" mockups (the interface of Souk Atlas is in French):

┌─────────────────────────────────────┐   ┌─────────────────────────────────────┐
│ SOUK ATLAS   [Rechercher…   ] 🛒 2  │   │ ← Catalogue                         │
├─────────────────────────────────────┤   │ ┌──────────┐  Bol en céramique Safi │
│ Tapis · Poterie · Cuir · Bijoux     │   │ │  photo   │  150,00 DH             │
│ ┌──────┐ ┌──────┐ ┌──────┐          │   │ └──────────┘  Atelier Belkhayat     │
│ │photo │ │photo │ │photo │          │   │  Quantité [1]  [Ajouter au panier]  │
│ │Tapis │ │Bol   │ │Babou-│          │   │  Description…                       │
│ │4500DH│ │150 DH│ │120 DH│          │   └─────────────────────────────────────┘
│ └──────┘ └──────┘ └──────┘          │        2. Fiche produit
│        ‹ 1 2 3 ›                    │
└─────────────────────────────────────┘
          1. Catalogue
┌─────────────────────────────────────┐   ┌─────────────────────────────────────┐
│ Panier                              │   │ Commande SA-2026-000123-77          │
│ Bol de Safi        x2    300,00 DH  │   │ À virer : 450,00 DH                 │
│ Babouches          x1    120,00 DH  │   │ RIB : 011 780 0000123456789012 34   │
│ Livraison                 30,00 DH  │   │ Motif : SA-2026-000123-77           │
│ Total (dont TVA 70 DH)   450,00 DH  │   │ Statut : en attente du virement     │
│ [Nom] [Téléphone] [Adresse] [Ville] │   └─────────────────────────────────────┘
│ [Valider la commande]               │        4. Confirmation
└─────────────────────────────────────┘
          3. Panier et livraison           5. Espace vendeur : mes produits, mes commandes

The five screens are: 1. Catalog, 2. Product page, 3. Cart and delivery, 4. Confirmation (amount to transfer, RIB, transfer description, status "waiting for the transfer"), 5. Seller area (my products, my orders).

Wireframes reveal questions that text hides: do you need an account to order? (yes, to follow your order); is delivery free? (above 500 DH); are prices VAT-inclusive? (yes, as in all retail in Morocco; the 20% VAT is included and displayed for information).

Write these decisions down in docs/: in three months, nobody will remember them.

5. The architecture

 Browser (phone, PC)
 ┌────────────────────────┐   HTTPS + JSON    ┌──────────────────────────┐    SQL     ┌──────────────┐
 │  React front (Vite)    │ ────────────────▶ │  Node.js / Express API   │ ─────────▶ │  PostgreSQL  │
 │  catalog, cart,        │ ◀──────────────── │  validation, prices,     │ ◀───────── │  tables,     │
 │  seller area           │   session cookie  │  stock, sessions, orders │            │  constraints │
 └────────────────────────┘                   └──────────────────────────┘            └──────────────┘
        static files served by nginx                port 3000 (local)                  port 5432 (local)

Each building block has one responsibility:

  • The front end displays and collects. It can calculate a total "for information", but it has no authority: a clever user can modify any calculation made in their browser.
  • The API applies the rules: it reads the prices again from the database, checks the stock, controls who is allowed to do what, and refuses everything else with a clear error code.
  • The database keeps the data and protects its consistency with constraints (a stock cannot be negative, an order always belongs to an existing buyer).

Why these technologies? Not because they are fashionable: React has a huge ecosystem and developers available in Morocco; Express is the most widespread and best-documented Node.js server; PostgreSQL offers solid transactions, which are essential as soon as you handle money and stock. Another team could have chosen something else; what matters is being able to justify your choice.

The React documentation website offers a method for breaking a user interface down into components from a wireframe ("Thinking in React"): this is exactly what we will do in level 3.

6. The Git repository

A single repository for the whole project:

souk-atlas/
├── README.md            ← what the project is, how to run it
├── .gitignore           ← node_modules/, .env, dist/…
├── .env.example         ← the expected variables, WITHOUT secret values
├── compose.yaml         ← (level 4) the complete stack
├── docs/
│   ├── BESOINS.md       ← user stories, out of scope, decisions
│   ├── API.md           ← (lesson 1.3) the API contract
│   └── maquettes/
├── api/                 ← the Node.js back end
│   ├── package.json
│   ├── migrations/      ← (lesson 1.2) the SQL schema, versioned
│   ├── src/
│   └── test/
└── web/                 ← the React front end
    ├── package.json
    └── src/

Create it:

mkdir -p souk-atlas/{api/src,web/src,docs}
cd souk-atlas
touch api/src/.gitkeep web/src/.gitkeep     # Git does not track empty folders
printf '%s\n' 'node_modules/' '.env' 'dist/' 'coverage/' '*.log' > .gitignore
git init -b main
git add .
git commit -m "Initialisation du projet Souk Atlas"

Secrets: .env and .env.example

The API will need a database address and a session secret. These values live in a .env file, which is different on each machine and never committed:

# .env.example  (versioned: it documents what must be filled in)
NODE_ENV=development
PORT=3000
DATABASE_URL=postgres://souk:souk@localhost:5432/souk_atlas
SESSION_SECRET=

Each developer copies the template with cp .env.example .env, then fills in their own values. Check that it is really ignored:

git status --short        # .env must NOT appear
git check-ignore -v .env  # shows the .gitignore rule that ignores it

A password pushed to GitHub, even for one minute, must be considered compromised: you must change it. The Git history keeps everything.

How to work

For a small team: the main branch is always deliverable; each feature is developed on a branch (feature/panier), then reaches main through a pull request that is reviewed and tested automatically (lesson 4.2). Clear commit messages ("Add the delivery calculation") are better than a complicated convention that nobody follows.

7. The course plan

Level Lessons What you produce
1 Design 1.1 to 1.3 requirements, SQL schema, API contract
2 Back end 2.1 to 2.4 the API: skeleton, catalog, accounts, orders
3 Front end 3.1 and 3.2 the complete React front end
4 Going to production 4.1 to 4.3 tests, containers, CI, deployment on a VPS

The labs run in a Node.js container without Internet access: you cannot install Express, React or PostgreSQL there. So you will write the project's business building blocks (validation, cart calculation, transfer reference, HTTP router, SQL schema…) using only the modules built into Node.js, and each check runs your code. These building blocks are exactly the ones you will then plug into Express and React on your own machine.

Common pitfalls

  • Coding before knowing what to code: without stories or wireframes, you discover the real questions (VAT-inclusive or not? account required?) in the middle of development, and you start again.
  • Forgetting the out of scope list: each meeting adds "just one small feature", and the project never ships.
  • Committing .env: add .env to .gitignore before the first git add ., not after.
  • Trusting the front end: a total, a price or a role sent by the browser can be modified by anyone with the developer tools.

Lab

You create the skeleton of the Souk Atlas repository in /root/souk-atlas: the api/, web/, docs/ tree and a README; the requirements document with its user stories and its out of scope list; the protection of secrets (.gitignore and .env.example); and finally the Git repository on the main branch with a first commit, checking that a real .env file stays out of Git.

Quiz

  1. Why must the browser not send the price of a product to the API when the order is placed?
  2. What is the difference between .env and .env.example?
  3. What is the "out of scope" list in the requirements document for?
  4. Why does Souk Atlas use an order reference in the transfer description?
  5. A colleague pushed the real database password in a commit, then removed it in the next commit. Is the problem solved?
Answers
  1. Because anything that comes from the browser can be modified by the user. The API receives only product identifiers and quantities, then reads the prices again from the database.
  2. .env contains the real values (password, secret) for this machine and is never versioned; .env.example is versioned, lists the expected variables and contains no secret value.
  3. It sets what will not be done in this version, to protect the deadline and avoid constant additions.
  4. So that the administrator can match each transfer received with the right order; the check digits (lesson 2.4) also detect typing errors.
  5. No: the Git history keeps the old commit. You must consider the password compromised and change it, then possibly clean up the history.

Practical exercises

To do on Node.js 22 (Debian, git, npm, python3)

The online terminal is coming soon. Meanwhile, do these tasks on your own Linux machine, a virtual machine or WSL.

  1. Create the project folder /root/souk-atlas with the subfolders api/src, web/src and docs, then a README.md that contains the name "Souk Atlas" and describes the project in two or three lines.
    Show a hintmkdir -p creates the whole tree at once; put an empty .gitkeep file in api/src and web/src so that Git keeps these folders.
    Show the solution
    mkdir -p /root/souk-atlas/api/src /root/souk-atlas/web/src /root/souk-atlas/docs && touch /root/souk-atlas/api/src/.gitkeep /root/souk-atlas/web/src/.gitkeep && mkdir -p /root/souk-atlas && printf '%s\n' '# Souk Atlas' '' 'Place de marché des artisans marocains : catalogue, panier en dirhams, commandes, espace vendeur, paiement par virement.' '' '## Dossiers' '' '- api/ : API Node.js (Express) et migrations PostgreSQL' '- web/ : application React' '- docs/ : besoins, maquettes, contrat de l'\''API' > /root/souk-atlas/README.md

    Check : cd /root/souk-atlas && test -d api/src && test -d web/src && test -d docs && grep -q "Souk Atlas" README.md

  2. Write /root/souk-atlas/docs/BESOINS.md with three level-2 headings: "## Acheteur" (buyer), "## Vendeur" (seller) and "## Hors périmètre" (out of scope), and at least four user stories as bullets that start with "- En tant qu…" ("En tant qu'acheteur…", "En tant que vendeur…"). Keep these French headings and bullet openings exactly as shown: the check looks for them.
    Show a hintA user story follows the pattern "As a <role>, I <action> (to <benefit>)", in French "En tant que <rôle>, je <action> (pour <bénéfice>)". Write the file with cat > fichier <<'FIN' … FIN.
    Show the solution
    mkdir -p /root/souk-atlas/docs && printf '%s\n' '# Souk Atlas — besoins' '' '## Acheteur' '' '- En tant qu'\''acheteur, je parcours le catalogue par catégorie et je cherche un produit par son nom.' '- En tant qu'\''acheteur, je remplis un panier et je vois le total en dirhams, livraison comprise.' '- En tant qu'\''acheteur, je passe commande et je reçois la référence à indiquer sur mon virement.' '' '## Vendeur' '' '- En tant que vendeur, j'\''ajoute, modifie et retire mes produits.' '- En tant que vendeur, je vois les commandes payées et je les marque expédiées.' '' '## Hors périmètre' '' '- Paiement par carte bancaire (prévu en version 2).' '- Application mobile native.' > /root/souk-atlas/docs/BESOINS.md

    Check : f=/root/souk-atlas/docs/BESOINS.md; grep -q "^## Acheteur" $f && grep -q "^## Vendeur" $f && grep -q "^## Hors p" $f && test "$(grep -c "^- En tant qu" $f)" -ge 4

  3. Prepare the secrets: a /root/souk-atlas/.gitignore file that ignores node_modules/ and .env (one entry per line), and a /root/souk-atlas/.env.example template that declares the variables DATABASE_URL= and SESSION_SECRET= (with no real secret value).
    Show a hintprintf '%s\n' 'node_modules/' '.env' > .gitignore writes two lines at once.
    Show the solution
    mkdir -p /root/souk-atlas && printf '%s\n' 'node_modules/' '.env' 'dist/' 'coverage/' '*.log' > /root/souk-atlas/.gitignore && mkdir -p /root/souk-atlas && printf '%s\n' '# Copiez ce fichier en .env et remplissez les valeurs' 'NODE_ENV=development' 'PORT=3000' 'DATABASE_URL=postgres://souk:souk@localhost:5432/souk_atlas' 'SESSION_SECRET=' > /root/souk-atlas/.env.example

    Check : cd /root/souk-atlas && grep -Eq "^/?node_modules/?$" .gitignore && grep -Eq "^\.env$" .gitignore && grep -q "^DATABASE_URL=" .env.example && grep -q "^SESSION_SECRET=" .env.example

  4. Create a local file /root/souk-atlas/.env (for example SESSION_SECRET=local), then initialize the Git repository on the main branch and make the first commit of the whole project. The .env file must exist on disk but must NOT be tracked by Git.
    Show a hintgit init -b main, git add ., then git commit -m "…". If Git asks for an identity: git config user.name "Your name" and git config user.email "you@exemple.ma".
    Show the solution
    cd /root/souk-atlas && echo "SESSION_SECRET=local" > .env && git init -q -b main && git add . && git -c user.name="Etudiant Souk Atlas" -c user.email="etudiant@exemple.ma" commit -q -m "Initialisation du projet Souk Atlas"

    Check : cd /root/souk-atlas && test -f .env && git rev-parse --verify HEAD >/dev/null 2>&1 && test "$(git branch --show-current)" = main && git ls-files | grep -qx README.md && git ls-files | grep -qx .gitignore && git ls-files | grep -qx docs/BESOINS.md && ! git ls-files | grep -qx .env

Official references consulted: Penser en React — https://fr.react.dev/learn/thinking-in-react ; Express : l'exemple « Hello world » — https://expressjs.com/fr/5x/starter/hello-world/ ; PostgreSQL — Architectural Fundamentals — https://www.postgresql.org/docs/current/tutorial-arch.html ; GitHub Actions — Intégration continue — https://docs.github.com/fr/actions/get-started/continuous-integration. Original MasterLinux text.