Skip to content

louisgavalda/web-applications-patterns

Repository files navigation

Web Applications Patterns

Web Applications Patterns course @ EPITA / January 2025 with Louis Gavalda.

About this course

Objectives

  • Discuss and learn about a few of the most common patterns used in web applications.
  • Build something together -- and by doing so, learn to apply them in your own projects.
  • Have fun.

And more precisely, we are going to build:

  • a Django web application for... 🥁 taking notes.
  • a REST API (using Django Ninja) on top of our Django application.
  • a basic SPA (using Svelte) for consuming content from our API.

Prerequisites

  • Be curious.
  • Be confident in your thinking/coding ability.
  • Be confortable using a terminal.
  • Know how to read (documentation).

Where to begin

Download or clone this repository: https://github.com/louisgavalda/web-applications-patterns

git clone https://github.com/louisgavalda/web-applications-patterns.git web-applications-patterns-git

Execute these commands in your terminal in order to install and run the Django app:

# Change directory, we go into to the project folder
cd web-applications-patterns-git
# Create a new Python virtual environment named 'venv'
python3 -m venv venv
# Activate the virtual environment
source ./venv/bin/activate
# Install all project dependencies listed in requirements.txt
pip install -r requirements.txt
# Generate database migration files (based on model changes)
./manage.py makemigrations
# Run the migrations in order to apply changes to the database; it gets created if it does not exist
# Create an admin user account with the specified (dummy) email
./manage.py createsuperuser --email user@example.com  # password
# Start the Django development server in debug mode
DEBUG=1 ./manage.py runserver

Open your browser and go to http://127.0.0.1:8000/

If everything is OK, you should see this message:

yes, it works! 👍

A few commonly used patterns

Model-View-Controller (MVC)

What?

The MVC (Model-View-Controller) pattern is a fundamental architectural pattern that divides a web application into three distinct but interconnected components:

(1) Model

The brain and data manager

The Model represents your application's data structure and business logic.

  • Manages data, business rules, logic, and operations
  • Interacts with your database
  • Maintains the integrity of your data
  • Is completely independent of the user interface

Example: In a blog application, the Model would handle:

  • Post creation and validation
  • User authentication rules
  • Comment management
  • Database interactions

(2) View

The face of your application

The View is responsible for presenting data to users.

  • Renders the user interface
  • Displays data in a human-readable format
  • Handles the visual presentation logic
  • Receives data from the Controller

Example: In a blog application, Views would include:

  • The homepage template showing recent posts
  • The user profile page layout
  • The comment submission form
  • The admin dashboard interface

(3) Controller

The traffic director

The Controller mediates between the Model and View.

  • Processes incoming requests from users
  • Coordinates between Model and View
  • Makes decisions about which data to fetch
  • Determines which View to display

Example: When a user creates a new blog post:

  1. The Controller receives the form submission
  2. Validates the input data
  3. Tells the Model to create a new post
  4. Chooses the appropriate View to display (success or error page)

Flow of Information

from Wikipédia

  1. User interacts with the View (clicks a button, submits a form)
  2. Controller receives the request
  3. Controller processes the request and interacts with the Model if needed
  4. Model performs business logic and data operations
  5. Controller receives results from the Model
  6. Controller selects and updates the appropriate View
  7. Updated View is presented to the user

Why?

This separation of concerns leads to:

  • More maintainable code
  • Easier debugging
  • Better code organization
  • Simpler testing
  • Enhanced scalability

How?

Example with the Python Django framework which is built around a MVC (or MTV?) pattern.

We will draw inspiration from the famous Django tutorial. Read the first part of the tutorial, and maybe the beginning of the second one if you're curious.

We will create an app for... 🥁 taking notes.

See also

RESTful Services

What?

Following is a practical explanation of what a REST API is (using our notes application as an example):

A REST API (Representational State Transfer Application Programming Interface) is a way for your frontend (i.e. Svelte) to communicate with your backend (i.e. Django) through HTTP requests. Think of it as a contract between frontend and backend that defines how they exchange data.

Here are the key concepts applied to our notes taking app:

Resources and Endpoints

Your data entities become URLs (endpoints):
- /api/notes/         → all notes
- /api/notes/42/      → specific note with ID 42
- /api/tags/          → all tags

HTTP Methods (CRUD operations)

Each endpoint supports specific operations:
GET     /api/notes/      → Retrieve all notes
POST    /api/notes/      → Create a new note
GET     /api/notes/42/   → Retrieve note 42
PUT     /api/notes/42/   → Update note 42
DELETE  /api/notes/42/   → Delete note 42

Request/Response Format (usually JSON)

// GET /api/notes/42/
{
    "id": 42,
    "title": "My Note",
    "content": "Some content",
    "tags": [
        {"id": 1, "name": "work"},
        {"id": 2, "name": "important"}
    ],
    "created_at": "2024-01-15T10:30:00Z",
    "updated_at": "2024-01-15T10:30:00Z"
}

Query Parameters (for filtering/sorting)

GET /api/notes/?search=python     → Search notes containing "python"
GET /api/notes/?tag=work          → Filter notes with "work" tag
GET /api/notes/?sort=updated_at   → Sort notes by update date

Status Codes (response outcomes)

200 OK            → Request successful
201 Created       → New note created successfully
400 Bad Request   → Invalid data sent
404 Not Found     → Note doesn't exist
500 Server Error  → Something went wrong on the server

Example Frontend Usage (Svelte):

// Fetching all notes
async function getNotes() {
    const response = await fetch('/api/notes/');
    const notes = await response.json();
}

// Creating a new note
async function createNote(noteData) {
    const response = await fetch('/api/notes/', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify(noteData)
    });
    const newNote = await response.json();
}

Best Practices:

  • Use plural nouns for resources (/notes/ not /note/)
  • Nest related resources logically (/notes/42/tags/)
  • Use query parameters for filtering/sorting
  • Return appropriate status codes
  • Include error messages in responses
  • Version your API if needed (/api/v1/notes/)

This structure allows your Svelte frontend to interact with our Django backend in a standardized, predictable way, making it easier to develop and maintain our application.

Why?

Key Benefits:

  • Stateless: Each request contains all needed information
  • Cacheable: Responses can be cached for performance
  • Uniform Interface: Consistent way to handle all resources
  • Client-Server Separation: Frontend and backend can evolve independently

How?

We will use Django Ninja to add a REST to our Django application.

See the Django Ninja tutorial. Django Ninja is already installed, you don't need to take care of that. Simply draw inspiration from the CRUD example and try to adapt it for our notes taking application.

Single Page Application (SPA)

What?

A Single Page Application is a web application that loads a single HTML page and dynamically updates content as the user interacts with it, without requiring full page reloads. This creates a more fluid, desktop-like experience.

Why?

Key characteristics of a SPA:

  • Client-side routing
  • Dynamic content loading
  • Smooth transitions
  • State management
  • Reduced server load
  • Better user experience

How?

A few examples of popular SPA frameworks/libraries:

  1. React
    • Created by Facebook
    • Uses Virtual DOM
    • Large ecosystem
    • Popular features: Hooks, JSX
function App() {
  const [count, setCount] = useState(0)
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  )
}
  1. Vue.js
    • Progressive framework
    • Gentle learning curve
    • Two-way data binding
    • Template-based
<template>
  <div>
    <p>{{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
export default {
  data() {
    return { count: 0 }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}
</script>
  1. Svelte
    • Compile-time framework
    • No virtual DOM
    • Less boilerplate
    • Smaller bundle size
<script>
  let count = 0
  
  function increment() {
    count += 1
  }
</script>

<p>{count}</p>
<button on:click={increment}>
  Increment
</button>
  1. Angular
    • Full-featured framework by Google
    • TypeScript-based
    • Comprehensive tooling
    • Built-in dependency injection
@Component({
  selector: 'app-counter',
  template: `
    <p>{{count}}</p>
    <button (click)="increment()">Increment</button>
  `
})
export class CounterComponent {
  count = 0;
  
  increment() {
    this.count++;
  }
}
  1. Solid.js
    • React-like syntax
    • Fine-grained reactivity
    • Very performant
    • Small bundle size
function Counter() {
  const [count, setCount] = createSignal(0);
  
  return (
    <>
      <p>{count()}</p>
      <button onClick={() => setCount(c => c + 1)}>
        Increment
      </button>
    </>
  );
}

Key Considerations When Choosing a Framework:

  • Learning curve
  • Community size
  • Performance requirements
  • Bundle size
  • Development experience
  • Available tooling
  • Team expertise
  • Project scale
  • SEO requirements

Each framework has its strengths, and the choice often depends on specific project requirements, team expertise, and business needs.


For our project we will use Svelte (with SvelteKit) to build a SPA for our notes taking application.

Execute these commands in your terminal in order to install and run the Svelte application:

# Access the Svelte project directory.
cd svelte-app
# Install the JavaScript/Node dependencies.
npm install
# Run the Svelte app using the development server.
npm run dev

Observer Pattern

What?

The Observer Pattern is a behavioral design pattern that establishes a one-to-many relationship between objects, where when one object (the Subject/Observable) changes state, all its dependents (Observers) are notified and updated automatically. It's particularly useful for implementing distributed event handling systems.

Core Components:

  1. Subject/Observable: The object holding the state and maintaining a list of observers
  2. Observer: The object that wants to be notified of changes

Real-world Example:

Think of it like a YouTube channel subscription:

  • The YouTube channel is the Subject
  • Subscribers are the Observers
  • When a new video is posted (state change), all subscribers get notified

Modern Implementation Examples:

  1. React's useState Hook:
const [state, setState] = useState(initialState);
// Components re-render (observe) when state changes
  1. RxJS Observables:
const observable = new Observable(subscriber => {
    subscriber.next('Hello');
    subscriber.next('World');
});

observable.subscribe(value => console.log(value));
  1. EventEmitter in Node.js:
const eventEmitter = new EventEmitter();
eventEmitter.on('event', data => console.log(data));
eventEmitter.emit('event', 'Something happened!');

The Observer Pattern is particularly relevant in modern web development where real-time updates and reactive programming are common requirements. It's the foundation for many modern frameworks and libraries (like RxJS, for React) that handle state management and UI updates.

Why?

Common Use Cases:

  • Real-time data updates in UI components
  • Event handling systems
  • Message broadcasting
  • Stock market updates
  • Push notifications
  • Chat applications
  • Live data monitoring

Benefits:

  1. Loose coupling between subjects and observers
  2. Support for broadcast communication
  3. Dynamic relationships can be established at runtime
  4. Good for establishing real-time event handling

Considerations:

  1. Memory leaks if observers aren't properly detached
  2. Order of notification isn't guaranteed
  3. Cascade of updates can occur if not managed properly
  4. Potential performance impact with many observers

How?

Here's a simple dummy implementation in TypeScript:

// Observer interface
interface Observer {
    update(data: any): void;
}

// Subject interface
interface Subject {
    attach(observer: Observer): void;
    detach(observer: Observer): void;
    notify(data: any): void;
}

// Concrete Subject
class NewsAgency implements Subject {
    private observers: Observer[] = [];
    
    attach(observer: Observer): void {
        if (!this.observers.includes(observer)) {
            this.observers.push(observer);
        }
    }
    
    detach(observer: Observer): void {
        const index = this.observers.indexOf(observer);
        if (index !== -1) {
            this.observers.splice(index, 1);
        }
    }
    
    notify(data: any): void {
        for (const observer of this.observers) {
            observer.update(data);
        }
    }

    publishNews(news: string): void {
        console.log(`Publishing news: ${news}`);
        this.notify(news);
    }
}

// Concrete Observer
class NewsSubscriber implements Observer {
    private name: string;

    constructor(name: string) {
        this.name = name;
    }

    update(data: string): void {
        console.log(`${this.name} received news: ${data}`);
    }
}

See also

Microservices

What?

A monolithic application (like our typical Django app) is structured as a single, unified codebase where all functionality - user authentication, business logic, database operations, file handling, etc. - lives together. Think of it as a large building where all departments work under one roof.

For example, in a Django e-commerce site:

  • All code runs as one process
  • A single database handles all data
  • Components are tightly coupled
  • Scaling requires replicating the entire application
  • Simpler to develop and deploy initially

In contrast, a microservices architecture breaks down the application into smaller, independent services that each handle a specific business function. Each service:

  • Runs as its own process
  • Can use its own database
  • Communicates with other services via APIs (usually REST or gRPC)
  • Can be scaled independently
  • Can be written in different programming languages

For example, an e-commerce site using microservices might have:

  • An authentication service managing users
  • A product catalog service
  • An order processing service
  • A payment service
  • A shipping service

Why?

Each of these would run independently, with its own codebase and database, communicating through well-defined interfaces.

This provides better scalability and flexibility, but increases operational complexity and requires more sophisticated deployment and monitoring solutions.

Here is a breakdown of the advantages, disadvantages, and main tools used in microservices architecture.

Advantages:

  1. Fine-grained scalability: Each service can be scaled independently according to its needs
  2. Technological flexibility: Freedom to choose the best technology for each service
  3. Resilience: A service failure doesn't affect the entire system
  4. Independent deployment: Updates possible service by service
  5. Team organization: Each team can be autonomous with their service

Disadvantages:

  1. Increased operational complexity: More elements to manage and coordinate
  2. Higher cost: More complex infrastructure, more servers
  3. More difficult debugging: Issues can involve multiple services
  4. Data consistency: Challenge to maintain consistency between services
  5. Network latency: Inter-service communication adds latency

See this article from 2021: 10 Companies Using Microservices. Who is Using Them?

How?

Commonly used tools when building a microservices architecture are:

  1. Containerization and Orchestration:

    • Docker for containerization
    • Kubernetes for orchestration
    • Docker Compose for local development
  2. Service Discovery and Configuration:

    • Consul or Eureka for service discovery
    • etcd or ZooKeeper for distributed configuration
    • Spring Cloud Config for configuration management
  3. Communication:

    • REST or gRPC for APIs
    • RabbitMQ, Kafka for asynchronous messaging
    • GraphQL for API aggregation
  4. Monitoring and Observability:

    • Prometheus for metrics collection
    • Grafana for visualization
    • Jaeger or Zipkin for distributed tracing
    • ELK Stack (Elasticsearch, Logstash, Kibana) for logs
  5. CI/CD:

    • Jenkins, GitLab CI, or GitHub Actions
    • ArgoCD or Flux for GitOps
    • Helm for Kubernetes packaging
  6. Gateway and Security:

    • Kong or Traefik for API Gateway
    • Keycloak or Auth0 for authentication
    • Vault for secrets management
  7. Testing:

    • Pact for contract testing
    • Gatling or K6 for load testing
    • Chaos Monkey for resilience testing

Extra reading

About

Web Applications Patterns course @ EPITA - January 2025

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy