Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
8 views17 pages

Internet Technologies Lecture Notes 06

Uploaded by

meeesi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views17 pages

Internet Technologies Lecture Notes 06

Uploaded by

meeesi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 17

Lecture Notes: Web Application Architecture​

Dr. Hassan Eldeeb & Eng. Mostafa Abo Elnaga | Tanta University​
Course: Internet Technologies | Date: Feb, 2025

Introduction to Databases
Overview
Databases are essential for storing, retrieving, and managing data in a structured manner.
They are used in applications ranging from small-scale personal projects to enterprise-level
systems.

Types of Databases
●​ Relational Databases (SQL): Data is stored in structured tables with predefined
schemas.
●​ NoSQL Databases: Flexible schema design, often used for unstructured or
semi-structured data.

Relational Databases
Key Concepts
●​ Relational Model: Data is stored in tables with rows and columns.
●​ Relationships:
○​ One-to-One
○​ One-to-Many
○​ Many-to-Many
●​ Schema Enforcement:
○​ Defined data types
○​ Constraints (e.g., NOT NULL, UNIQUE)
●​ SQL (Structured Query Language):
○​ Data Definition Language (DDL): Creating and modifying schema.
○​ Data Manipulation Language (DML): Querying and updating records.
Benefits
●​ ACID Transactions: Ensures data integrity.
○​ Atomicity: Ensures all or nothing execution.
○​ Consistency: Data remains valid before and after transactions.
○​ Isolation: Transactions are independent of each other.
○​ Durability: Ensures committed transactions persist.
●​ Ideal for structured data and complex queries.

When to Use Relational Databases


●​ Applications requiring strict consistency (e.g., financial systems).
●​ Scenarios involving complex joins and structured relationships.

Example: Creating a MySQL Table


CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

NoSQL Databases
Key Concepts
●​ Document-Oriented Storage: Data is stored as JSON-like documents.
●​ Dynamic Schema: Fields can vary across documents.
●​ Horizontal Scaling: Easily distributes data across multiple servers.

Benefits
●​ Flexible schema evolution.
●​ Efficient handling of nested objects.

When to Use NoSQL Databases


●​ Quick prototyping.
●​ Handling large-scale applications with unstructured or semi-structured data.

Example: MongoDB Document


{
"_id": "object_id_here",
"name": "Alice",
"email": "[email protected]",
"interests": ["reading", "gaming"],
"createdAt": "2025-02-17T12:34:56Z"
}

ORM vs. Query Builders


Object-Relational Mapping (ORM)
●​ Maps database tables to objects in code.
●​ Popular ORMs:
○​ Sequelize (PostgreSQL, MySQL, SQLite, MSSQL)
○​ Prisma (TypeScript-first approach, strong type safety)
○​ Mongoose (MongoDB ODM, schema definitions, and validation)

Pros

●​ Reduces repetitive SQL.


●​ Enforces consistent schema.

Cons

●​ Overhead for complex queries.


●​ Learning curve.

Query Builders
●​ Constructs SQL queries programmatically.
●​ Example: Knex.js for Node.js:

const knex = require('knex')({


client: 'pg',
connection: {
host: '127.0.0.1',
user: 'postgres',
password: 'password',
database: 'mydb'
}
});
knex('users').select('*').then(rows => console.log(rows));

Pros

●​ More control over queries.


●​ Less overhead.

Cons

●​ Fewer high-level abstractions.

Designing a Database Schema


Steps
1.​ Requirements Gathering:
○​ Identify entities (User, Product, Post, Comment).
○​ Define relationships (1:N, N:N).
2.​ Choosing SQL or NoSQL:
○​ SQL: Normalized structure, strict schema.
○​ NoSQL: Denormalized, flexible schema.

Example: Relational Schema for a Blog


CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE posts (


id SERIAL PRIMARY KEY,
title VARCHAR(200) NOT NULL,
content TEXT NOT NULL,
user_id INT REFERENCES users(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Example: NoSQL Schema (MongoDB)


const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({


name: String,
email: { type: String, unique: true },
createdAt: { type: Date, default: Date.now }
});

const postSchema = new mongoose.Schema({


title: String,
content: String,
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
createdAt: { type: Date, default: Date.now }
});

Implementing CRUD APIs


Express + Sequelize (PostgreSQL)
Install Dependencies
npm install express sequelize pg pg-hstore

Database Setup
const { Sequelize } = require('sequelize');
const sequelize = new Sequelize('mydb', 'postgres', 'password', {
host: 'localhost',
dialect: 'postgres'
});

User Model
const { DataTypes } = require('sequelize');
const sequelize = require('../db');

const User = sequelize.define('User', {


name: { type: DataTypes.STRING, allowNull: false },
email: { type: DataTypes.STRING, allowNull: false, unique: true }
});

CRUD Operations
const express = require('express');
const app = express();
const User = require('./models/User');

app.use(express.json());

app.post('/api/users', async (req, res) => {


try {
const newUser = await User.create(req.body);
res.status(201).json(newUser);
} catch (err) {
res.status(400).json({ error: err.message });
}
});

app.get('/api/users', async (req, res) => {


const users = await User.findAll();
res.json(users);
});

app.listen(3000, () => console.log('Server running at http://localhost:3000'));

Express + Mongoose (MongoDB)


Install Dependencies
npm install express mongoose

MongoDB Setup
const mongoose = require('mongoose');
const connectDB = async () => {
await mongoose.connect('mongodb://localhost:27017/mydb');
};

CRUD Operations
const express = require('express');
const User = require('./models/User');
const app = express();
app.use(express.json());
connectDB();

app.post('/api/users', async (req, res) => {


const newUser = await User.create(req.body);
res.status(201).json(newUser);
});

app.get('/api/users', async (req, res) => {


const users = await User.find();
res.json(users);
});

app.listen(3000, () => console.log('Server running at http://localhost:3000'));

A detailed example is presented in the


Appendix

Summary
●​ SQL: Structured, ACID compliance.
●​ NoSQL: Flexible, scalable.
●​ ORM vs. Query Builder trade-offs.
●​ Designing schemas effectively.
●​ Implementing CRUD APIs with PostgreSQL & MongoDB.

Contact:

●​ Email: [email protected]
●​ Email: [email protected]
Appendix:

Example system overview: E-Commerce


Consider a simple e-commerce application called “ShopSimple” where users can browse
products, add them to an order, and make a purchase.

●​ Users can register and log in.


●​ Products can be categorized.
●​ Orders are created by users and contain multiple order items (each referencing a
product).

High-level requirements
1.​ User management
○​ Each user has a unique email and password (stored securely, e.g., hashed).
○​ a user can have many orders.
2.​ Catalog management
○​ products each have a name, description, price.
○​ products belong to one or more categories (optional complexity: a product
might be in multiple categories, or keep it simple with a one-to-one
relationship?).
3.​ Order processing
○​ An Order references a user and has a status (e.g., pending, completed,
canceled).
○​ Order Items (line items) reference both the Order and a Product, including a
quantity.
4.​ Checkout Flow (high-level)
○​ Payment and shipping details can be stored, but to keep it simple we’ll
skip/ignore advanced payment workflows.

Entity-Relationship Diagram (ERD)


Below is an ERD for ShopSimple (using a relational approach).

Link: here (via Mermaid Live Editor)

Relationships:

users : orders = 1 : N

orders : order_items = 1 : N

products : order_items = 1 : N

products : categories = M : N

○​ Achieved via a junction table (e.g., product_categories(product_id,


category_id)).

Schema definitions (PostgreSQL + Sequelize)


Below is an example of how you might define these tables in a relational context. We’ll use
Sequelize to keep the code consistent with the Node.js environment.
Install & initialize

```bash​
​ npm install express sequelize pg pg-hstore​
```

```js

// db.js (Sequelize connection)

const { Sequelize } = require('sequelize');

const sequelize = new Sequelize('shopsimple_db', 'postgres',


'password', {​
​ host: 'localhost',​
​ dialect: 'postgres',​
});​
module.exports = sequelize;

```

Models: Users, Products, Categories

```js

// models/User.js

const { DataTypes } = require('sequelize');

const sequelize = require('../db');

const User = sequelize.define('User', {​


​ email: {​
​ ​ type: DataTypes.STRING,​
​ ​ allowNull: false,​
​ ​ unique: true,​
​ },​
​ password: {​
​ ​ type: DataTypes.STRING,​
​ ​ allowNull: false,​
​ },​
});

module.exports = User;

```
```js

// models/Product.js

const { DataTypes } = require('sequelize');​


const sequelize = require('../db');

const Product = sequelize.define('Product', {​


​ name: {​
​ ​ type: DataTypes.STRING,​
​ ​ allowNull: false,​
​ },​
​ price: {​
​ ​ type: DataTypes.DECIMAL(10, 2), // e.g. 99999999.99​
​ ​ allowNull: false,​
​ },​
});

module.exports = Product;

```

```js

// models/Category.js​
const { DataTypes } = require('sequelize');​
const sequelize = require('../db');​

const Category = sequelize.define('Category', {​
​ name: {​
​ ​ type: DataTypes.STRING,​
​ ​ allowNull: false,​
​ },​
});

module.exports = Category;

```


Models: Orders & OrderItems
```js

// models/Order.js​
const { DataTypes } = require('sequelize');​
const sequelize = require('../db');​

const Order = sequelize.define('Order', {​
​ status: {​
​ ​ type: DataTypes.ENUM('pending', 'completed',
'canceled'),​
​ ​ allowNull: false,​
​ ​ defaultValue: 'pending',​
​ },​
});

module.exports = Order;

```​

```js

// models/OrderItem.js​
const { DataTypes } = require('sequelize');​
const sequelize = require('../db');

const OrderItem = sequelize.define('OrderItem', {​


​ quantity: {​
​ ​ type: DataTypes.INTEGER,​
​ ​ allowNull: false,​
​ ​ defaultValue: 1,​
​ },​
});

module.exports = OrderItem;

```

Model associations

In Sequelize, you define relationships after all models are initialized. Typically, you can place
this in an associations.js file or near the model definitions.

```js

// models/index.js (or associations.js)


const User = require('./User');​
const Order = require('./Order');​
const OrderItem = require('./OrderItem');​
const Product = require('./Product');​
const Category = require('./Category');​
const sequelize = require('../db');

// 1) A User has many Orders​


User.hasMany(Order, { foreignKey: 'user_id' });​
Order.belongsTo(User, { foreignKey: 'user_id' });

// 2) An Order has many OrderItems​


Order.hasMany(OrderItem, { foreignKey: 'order_id' });​
OrderItem.belongsTo(Order, { foreignKey: 'order_id' });

// 3) A Product has many OrderItems​


Product.hasMany(OrderItem, { foreignKey: 'product_id' });​
OrderItem.belongsTo(Product, { foreignKey: 'product_id' });

// 4) Products <-> Categories (M:N)​


Product.belongsToMany(Category, {​
​ through: 'product_categories',​
​ foreignKey: 'product_id',​
​ otherKey: 'category_id',​
});

Category.belongsToMany(Product, {​
​ through: 'product_categories',​
​ foreignKey: 'category_id',​
​ otherKey: 'product_id',​
});

// Sync all

const initDB = async () => {​


​ try {​
​ ​ await sequelize.sync({ alter: true }); // 'alter'
for dev only, not recommended in production​
​ ​ console.log('All models were synchronized
successfully.');​
​ } catch (error) {​
​ ​ console.error('DB sync error:', error);​
​ }​
};

module.exports = { initDB, User, Order, OrderItem, Product,


Category };

Considerations & Concerns


●​ ACID & transactions​
- In an e-commerce setting, you must ensure an order is created with correct line
items.​
- Consider using Sequelize transactions for operations that must all succeed or fail
together.
●​ Indexing​
- For frequently searched fields (like product name), add indexes to speed up queries.
●​ Scaling​
- If read/write volume grows, consider load balancers, read replicas, or microservices
splitting inventory management, user management, etc.
●​ Data consistency​
- With many concurrent orders, ensure product availability is updated properly to
avoid overselling (beyond scope here, but relevant in real e-commerce).
●​ Security​
- Passwords must be hashed (e.g., using bcrypt).​
- Use environment variables to store DB credentials, never commit them to source
control.
Sample code: setting up CRUD endpoints
Below is a brief example showing a few routes for User and Product. ​

```js

// index.js​
const express = require('express');​
const { initDB, User, Product } = require('./models');​

const app = express();​
app.use(express.json());​

// Initialize DB (sync models)​
initDB();



// Create a new user​
app.post('/api/users', async (req, res) => {​
​ try {​
​ ​ // real app: hash password here with bcrypt​
​ ​ const newUser = await User.create(req.body);​
​ ​ res.status(201).json(newUser);​
​ } catch (error) {​
​ ​ res.status(400).json({ error: error.message });​
​ }​
});

// Get all users​


​ app.get('/api/users', async (req, res) => {​
​ ​ try {​
​ ​ ​ const users = await User.findAll();​
​ ​ ​ res.json(users);​
​ ​ } catch (error) {​
​ ​ ​ res.status(500).json({ error: error.message
});​
​ ​ }​
​ });

// Create a new product

app.post('/api/products', async (req, res) => {​


​ try {​
​ ​ const newProduct = await Product.create(req.body);​
​ ​ res.status(201).json(newProduct);​
​ } catch (error) {​
​ ​ res.status(400).json({ error: error.message });​
​ }​
});

// Get all products

app.get('/api/products', async (req, res) => {​


​ try {​
​ ​ const products = await Product.findAll();​
​ ​ res.json(products);​
​ } catch (error) {​
​ ​ res.status(500).json({ error: error.message });​
​ }​
});

app.listen(4000, () => {​
​ console.log('ShopSimple backend running at
http://localhost:4000');​
});​
```​
Notes:

●​ In a real production environment, we would add authentication, authorization,


more advanced error handling, and possibly pagination on product listings.
●​ For Orders and OrderItems, we would also create their CRUD operations /
endpoints for “Add to Cart”, “Checkout”, and so on.

You might also like