Настройка DynamoDB для веб-приложения

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Настройка DynamoDB для веб-приложения
Сложная
~2-3 рабочих дня
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Настройка DynamoDB для веб-приложения

DynamoDB — управляемая NoSQL база от AWS с гарантированной latency в миллисекунды при любом масштабе. Нет серверов для обслуживания, нет ручного шардирования, нет проблем с replica lag. Идеально для serverless-архитектур и приложений с непредсказуемыми пиками нагрузки.

Ключевые концепции перед проектированием

DynamoDB — не реляционная база и не MongoDB. Здесь нет JOIN, нет агрегатных запросов без полного скана, нет гибких фильтров. Все запросы строятся вокруг Partition Key (PK) и Sort Key (SK). Single-table design — стандартный подход: одна таблица для всех сущностей приложения с перегруженными PK/SK.

Создание таблицы через AWS CDK (TypeScript)

import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'
import { RemovalPolicy } from 'aws-cdk-lib'

const table = new dynamodb.Table(this, 'AppTable', {
  tableName: 'MyApp',
  partitionKey: { name: 'PK', type: dynamodb.AttributeType.STRING },
  sortKey: { name: 'SK', type: dynamodb.AttributeType.STRING },
  billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,  // или PROVISIONED + auto scaling
  pointInTimeRecovery: true,
  deletionProtection: true,
  removalPolicy: RemovalPolicy.RETAIN,

  // Streams для реакции на изменения
  stream: dynamodb.StreamViewType.NEW_AND_OLD_IMAGES,
})

// GSI для поиска по email
table.addGlobalSecondaryIndex({
  indexName: 'GSI1',
  partitionKey: { name: 'GSI1PK', type: dynamodb.AttributeType.STRING },
  sortKey: { name: 'GSI1SK', type: dynamodb.AttributeType.STRING },
  projectionType: dynamodb.ProjectionType.ALL,
})

Single-table design: пример схемы

Сущность: User
PK: USER#<userId>    SK: METADATA
GSI1PK: EMAIL#<email>   GSI1SK: USER#<userId>

Сущность: Order
PK: USER#<userId>    SK: ORDER#<orderId>
GSI1PK: ORDER#<orderId> GSI1SK: USER#<userId>

Сущность: OrderItem
PK: ORDER#<orderId>  SK: ITEM#<itemId>

Сущность: Product
PK: PRODUCT#<productId>  SK: METADATA
GSI1PK: CATEGORY#<cat>   GSI1SK: PRODUCT#<productId>

DynamoDB клиент (AWS SDK v3)

import { DynamoDBClient } from '@aws-sdk/client-dynamodb'
import {
  DynamoDBDocumentClient,
  GetCommand,
  PutCommand,
  QueryCommand,
  UpdateCommand,
  DeleteCommand,
  TransactWriteCommand
} from '@aws-sdk/lib-dynamodb'

const client = new DynamoDBClient({ region: process.env.AWS_REGION })
const db = DynamoDBDocumentClient.from(client, {
  marshallOptions: { removeUndefinedValues: true }
})

const TABLE = 'MyApp'

Репозиторий пользователей

export class UserRepository {
  async create(user: CreateUserInput): Promise<User> {
    const id = crypto.randomUUID()
    const now = new Date().toISOString()

    await db.send(new TransactWriteCommand({
      TransactItems: [
        {
          Put: {
            TableName: TABLE,
            Item: {
              PK: `USER#${id}`,
              SK: 'METADATA',
              GSI1PK: `EMAIL#${user.email}`,
              GSI1SK: `USER#${id}`,
              id,
              email: user.email,
              name: user.name,
              passwordHash: user.passwordHash,
              role: 'user',
              createdAt: now,
              updatedAt: now,
              _type: 'User'
            },
            ConditionExpression: 'attribute_not_exists(PK)'
          }
        }
      ]
    }))

    return { id, ...user, role: 'user', createdAt: now, updatedAt: now }
  }

  async findByEmail(email: string): Promise<User | null> {
    const result = await db.send(new QueryCommand({
      TableName: TABLE,
      IndexName: 'GSI1',
      KeyConditionExpression: 'GSI1PK = :pk',
      ExpressionAttributeValues: { ':pk': `EMAIL#${email}` },
      Limit: 1
    }))

    return (result.Items?.[0] as User) ?? null
  }

  async findById(id: string): Promise<User | null> {
    const result = await db.send(new GetCommand({
      TableName: TABLE,
      Key: { PK: `USER#${id}`, SK: 'METADATA' }
    }))

    return (result.Item as User) ?? null
  }
}

Заказы с пагинацией

export class OrderRepository {
  async listForUser(userId: string, limit = 25, lastKey?: Record<string, unknown>) {
    const result = await db.send(new QueryCommand({
      TableName: TABLE,
      KeyConditionExpression: 'PK = :pk AND begins_with(SK, :prefix)',
      ExpressionAttributeValues: {
        ':pk': `USER#${userId}`,
        ':prefix': 'ORDER#'
      },
      ScanIndexForward: false,  // новые сначала
      Limit: limit,
      ExclusiveStartKey: lastKey
    }))

    return {
      items: result.Items as Order[],
      nextKey: result.LastEvaluatedKey
    }
  }

  async createWithItems(order: CreateOrderInput): Promise<Order> {
    const orderId = crypto.randomUUID()
    const now = new Date().toISOString()

    const items: TransactWriteItem[] = [
      {
        Put: {
          TableName: TABLE,
          Item: {
            PK: `USER#${order.userId}`,
            SK: `ORDER#${now}#${orderId}`,  // now в SK для сортировки по дате
            GSI1PK: `ORDER#${orderId}`,
            GSI1SK: `USER#${order.userId}`,
            id: orderId,
            userId: order.userId,
            status: 'pending',
            total: order.total,
            createdAt: now,
            _type: 'Order'
          }
        }
      },
      ...order.items.map(item => ({
        Put: {
          TableName: TABLE,
          Item: {
            PK: `ORDER#${orderId}`,
            SK: `ITEM#${item.productId}`,
            orderId,
            productId: item.productId,
            quantity: item.quantity,
            price: item.price,
            _type: 'OrderItem'
          }
        }
      }))
    ]

    await db.send(new TransactWriteCommand({ TransactItems: items }))
    return { id: orderId, ...order, status: 'pending', createdAt: now }
  }
}

Настройка DynamoDB Streams + Lambda

// Обработка изменений в реальном времени
export const handler = async (event: DynamoDBStreamEvent) => {
  for (const record of event.Records) {
    if (record.eventName !== 'MODIFY') continue

    const newImage = unmarshall(record.dynamodb!.NewImage!)
    const oldImage = unmarshall(record.dynamodb!.OldImage!)

    if (newImage._type === 'Order' && newImage.status !== oldImage.status) {
      await notifyOrderStatusChange(newImage.id, newImage.status)
    }
  }
}

Мониторинг через CloudWatch

Ключевые метрики: ConsumedReadCapacityUnits, ConsumedWriteCapacityUnits, SuccessfulRequestLatency, SystemErrors, ThrottledRequests. На ThrottledRequests > 0 — немедленный алерт.

Сроки

Проектирование single-table schema + базовая интеграция с Node.js/Python: 3–5 дней. Добавление GSI, Streams, Lambda-обработчиков и мониторинга: ещё 3–5 дней. Миграция из реляционной базы в DynamoDB с пересмотром модели данных: 2–4 недели — это не механическая конвертация, а переосмысление access patterns.