nosql-database-design

Category: Database storage
Tags:
go mongodb data
For: Claude Code

Design NoSQL database schemas for MongoDB and DynamoDB. Use when modeling document structures, designing collections, or planning NoSQL data architectures.

Installation

Copy to your project
cp -r skills/nosql-database-design/ /your-project/.claude/skills/nosql-database-design/

NoSQL Database Design

Overview

Design scalable NoSQL schemas for MongoDB (document) and DynamoDB (key-value). Covers data modeling patterns, denormalization strategies, and query optimization for NoSQL systems.

When to Use

  • MongoDB collection design
  • DynamoDB table and index design
  • Document structure modeling
  • Embedding vs. referencing decisions
  • Query pattern optimization
  • NoSQL indexing strategies
  • Data denormalization planning

MongoDB Schema Design

Document Structure Design

MongoDB - Embedded Documents:

// Single document with embedded arrays
db.createCollection("users");

db.users.insertOne({
  _id: ObjectId("..."),
  email: "john@example.com",
  name: "John Doe",
  createdAt: new Date(),

  // Embedded address
  address: {
    street: "123 Main St",
    city: "New York",
    state: "NY",
    zipCode: "10001",
  },

  // Embedded array of items
  orders: [
    {
      orderId: ObjectId("..."),
      date: new Date(),
      total: 149.99,
    },
    {
      orderId: ObjectId("..."),
      date: new Date(),
      total: 89.99,
    },
  ],
});

MongoDB - Referenced Documents:

// Separate collections with references
db.createCollection("users");
db.createCollection("orders");

db.users.insertOne({
  _id: ObjectId("..."),
  email: "john@example.com",
  name: "John Doe",
});

db.orders.insertMany([
  {
    _id: ObjectId("..."),
    userId: ObjectId("..."), // Reference to user
    orderDate: new Date(),
    total: 149.99,
  },
  {
    _id: ObjectId("..."),
    userId: ObjectId("..."),
    orderDate: new Date(),
    total: 89.99,
  },
]);

// Query with $lookup for JOINs
db.orders.aggregate([
  {
    $match: { userId: ObjectId("...") },
  },
  {
    $lookup: {
      from: "users",
      localField: "userId",
      foreignField: "_id",
      as: "user",
    },
  },
]);

Indexing in MongoDB

// Single field index
db.users.createIndex({ email: 1 });
db.orders.createIndex({ createdAt: -1 });

// Compound index
db.orders.createIndex({ userId: 1, createdAt: -1 });

// Text index for search
db.products.createIndex({ name: "text", description: "text" });

// Geospatial index
db.stores.createIndex({ location: "2dsphere" });

// TTL index for auto-expiration
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 });

// Sparse index (only documents with field)
db.users.createIndex({ phone: 1 }, { sparse: true });

// Check index usage
db.users.aggregate([{ $indexStats: {} }]);

Schema Validation

// Define collection validation schema
db.createCollection("products", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "price", "category"],
      properties: {
        _id: { bsonType: "objectId" },
        name: {
          bsonType: "string",
          description: "Product name (required)",
        },
        price: {
          bsonType: "decimal",
          minimum: 0,
          description: "Price must be positive",
        },
        category: {
          enum: ["electronics", "clothing", "food"],
          description: "Category must be one of listed values",
        },
        tags: {
          bsonType: "array",
          items: { bsonType: "string" },
        },
        createdAt: {
          bsonType: "date",
        },
      },
    },
  },
});

DynamoDB Schema Design

Table Structure

// DynamoDB table with single primary key
const TableName = "users";
const params = {
  TableName,
  KeySchema: [
    { AttributeName: "userId", KeyType: "HASH" }, // Partition key
  ],
  AttributeDefinitions: [
    { AttributeName: "userId", AttributeType: "S" }, // String
  ],
  BillingMode: "PAY_PER_REQUEST", // On-demand
};

// DynamoDB table with composite primary key
const ordersParams = {
  TableName: "orders",
  KeySchema: [
    { AttributeName: "userId", KeyType: "HASH" }, // Partition key
    { AttributeName: "orderId", KeyType: "RANGE" }, // Sort key
  ],
  AttributeDefinitions: [
    { AttributeName: "userId", AttributeType: "S" },
    { AttributeName: "orderId", AttributeType: "S" },
  ],
  BillingMode: "PAY_PER_REQUEST",
};

Global Secondary Indexes (GSI)

// Add GSI for querying by email
const gsiParams = {
  TableName: "users",
  AttributeDefinitions: [{ AttributeName: "email", AttributeType: "S" }],
  GlobalSecondaryIndexes: [
    {
      IndexName: "emailIndex",
      KeySchema: [{ AttributeName: "email", KeyType: "HASH" }],
      Projection: {
        ProjectionType: "ALL", // Return all attributes
      },
      BillingMode: "PAY_PER_REQUEST",
    },
  ],
};

// GSI with composite key for time-based queries
const timeIndexParams = {
  GlobalSecondaryIndexes: [
    {
      IndexName: "userCreatedIndex",
      KeySchema: [
        { AttributeName: "userId", KeyType: "HASH" },
        { AttributeName: "createdAt", KeyType: "RANGE" },
      ],
      Projection: { ProjectionType: "ALL" },
      BillingMode: "PAY_PER_REQUEST",
    },
  ],
};

DynamoDB Item Operations

// Put item (insert/update)
const putParams = {
  TableName: "users",
  Item: {
    userId: { S: "user-123" },
    email: { S: "john@example.com" },
    name: { S: "John Doe" },
    createdAt: { N: Date.now().toString() },
    metadata: {
      M: {
        joinDate: { N: Date.now().toString() },
        source: { S: "web" },
      },
    },
  },
};

// Query using GSI
const queryParams = {
  TableName: "users",
  IndexName: "emailIndex",
  KeyConditionExpression: "email = :email",
  ExpressionAttributeValues: {
    ":email": { S: "john@example.com" },
  },
};

// Batch get items
const batchGetParams = {
  RequestItems: {
    users: {
      Keys: [{ userId: { S: "user-123" } }, { userId: { S: "user-456" } }],
    },
  },
};

Denormalization Patterns

MongoDB - Embedding for Performance:

// Embed frequently accessed data to avoid lookups
db.orders.insertOne({
  _id: ObjectId("..."),
  userId: ObjectId("..."),
  userEmail: "john@example.com", // Denormalized
  userName: "John Doe", // Denormalized
  createdAt: new Date(),
  items: [
    {
      productId: ObjectId("..."),
      productName: "Laptop", // Denormalized
      productPrice: 999.99, // Denormalized
      quantity: 1,
    },
  ],
});

DynamoDB - Denormalization with Consistency:

// Store related data in same item to ensure consistency
const params = {
  TableName: "orders",
  Item: {
    userId: { S: "user-123" },
    orderId: { S: "order-456" },
    orderDate: { N: Date.now().toString() },

    // User data snapshot at order time
    userSnapshot: {
      M: {
        email: { S: "john@example.com" },
        address: { S: "123 Main St" },
      },
    },

    // Items with product information
    items: {
      L: [
        {
          M: {
            productId: { S: "prod-789" },
            name: { S: "Laptop" },
            price: { N: "999.99" },
            quantity: { N: "1" },
          },
        },
      ],
    },
  },
};

Design Patterns

MongoDB - Time-Series Pattern:

// Efficient time-series data storage
db.sensor_data.insertOne({
  _id: ObjectId("..."),
  sensorId: "sensor-123",
  date: ISODate("2024-01-15"),
  measurements: [
    { time: "12:00", temperature: 72.5, humidity: 45 },
    { time: "12:01", temperature: 72.6, humidity: 45.2 },
    { time: "12:02", temperature: 72.4, humidity: 44.8 },
  ],
});

// Index for efficient queries
db.sensor_data.createIndex({ sensorId: 1, date: -1 });

DynamoDB - One-to-Many Relationship:

// Store one-to-many relationships efficiently
// User comments using userId as partition key, commentId as sort key
const commentParams = {
  TableName: "comments",
  Item: {
    userId: { S: "user-123" }, // Partition key
    commentId: { S: "comment-789" }, // Sort key
    postId: { S: "post-456" },
    content: { S: "Great article!" },
    createdAt: { N: Date.now().toString() },
  },
};

Capacity Planning

MongoDB - Horizontal Scaling:

// Sharding for large collections
sh.shardCollection("ecommerce.orders", { userId: "hashed" });

// Monitor shard distribution
db.orders.aggregate([
  { $group: { _id: "$userId", count: { $sum: 1 } } },
  { $sort: { count: -1 } },
]);

DynamoDB - Partition Key Design:

// Good: Distribute across many keys
// Partition key: "USER#123" (spreads across partitions)
// Sort key: "ORDER#2024-01"

// Bad: Hot partition
// Partition key: "ADMIN" (all admin operations hit same partition)

// Solution: Add timestamp or random suffix
// Partition key: "ADMIN#20240115#random"

Migration Considerations

  • Plan data migration strategy
  • Consider consistency requirements
  • Test query patterns before finalizing schema
  • Monitor performance after deployment
  • Document relationships and access patterns
  • Plan for schema evolution

Resources