承接 chrisjenkinson/dynamo-db-read-model 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

chrisjenkinson/dynamo-db-read-model

Composer 安装命令:

composer require chrisjenkinson/dynamo-db-read-model

包简介

A DynamoDB ReadModel implementation for Broadway.

README 文档

README

A DynamoDB-backed Broadway read model repository.

Installation

composer require chrisjenkinson/dynamo-db-read-model

The package supports async-aws/dynamo-db ^1.3, ^2.0, and ^3.0.

DynamoDB Table

Create one table for the read model store. The table uses a composite primary key:

Attribute Type Key Description
Name String Partition Repository name passed to RepositoryFactory::create()
Id String Sort Read model identifier from Identifiable::getId()
Data String - JSON-encoded serialized read model payload

The library queries by Name to load all read models for a repository, and uses Name + Id for single-item reads, writes, and deletes. It does not require secondary indexes.

The DynamoDB Id value is required to match the serialized read model's Identifiable::getId(). Rows where the physical key and payload id differ are treated as invalid stored data and rejected on read; repair those rows with explicit operational tooling rather than relying on repository normalization.

find($id) uses the full primary key (Name + Id) and is the bounded lookup to prefer for production read paths. findAll() queries the full repository partition for the configured Name and returns every read model in memory. findBy() also queries the full repository partition, deserializes each read model, and then filters in PHP. For production-sized collections, avoid using findAll() or findBy() on request paths unless that full-partition work is intentional.

Snapshot Store

This release changes the public constructors for DynamoDbRepositoryFactory and DynamoDbRepository.

DynamoDbRepositoryFactory requires a ReadModelSnapshotStore so repositories created by the same factory can suppress unchanged physical writes without an extra DynamoDB read:

$factory = new DynamoDbRepositoryFactory($client, $serializer, 'read-models', new ReadModelSnapshotStore());

The factory is the recommended construction path. It creates the per-repository storage and matcher internally.

Direct DynamoDbRepository construction is still possible, but its constructor now expects explicit storage and matcher collaborators. If you manually construct repositories, move the DynamoDB-specific arguments into a DynamoDbReadModelStorage configured for that table, repository name, and read model class, then pass that storage with a ReadModelFieldMatcher:

$storage = new DynamoDbReadModelStorage(
    $client,
    $inputBuilder,
    $serializer,
    $jsonEncoder,
    $jsonDecoder,
    'read-models',
    'repository-name',
    MyReadModel::class,
    new ReadModelSnapshotStore()
);

$repository = new DynamoDbRepository(
    $storage,
    new ReadModelFieldMatcher()
);

Call $factory->clearSnapshots() before reusing the same factory after deleting or recreating the backing read-model table.

Deferred Persistence

Repositories created with DynamoDbRepositoryFactory::create() keep the original immediate behaviour: save() writes with PutItem, remove() writes with DeleteItem, and find() reads with GetItem.

For replay or other batching-sensitive workflows, opt in explicitly to deferred persistence:

$repository = $factory->createDeferred('repository-name', MyReadModel::class);

$model = $repository->find($id) ?? new MyReadModel($id);

// Projector handlers can keep using Broadway\Repository methods.
$repository->save($model);

// Write pending removals and saves to DynamoDB.
$repository->flush();

createDeferred() is package-specific and is not part of Broadway's RepositoryFactory interface. Code that needs deferred repository creation should type-hint DeferredRepositoryFactory or the concrete DynamoDbRepositoryFactory.

A deferred repository still implements Broadway\ReadModel\Repository, and also implements FlushableRepository:

interface FlushableRepository extends Broadway\ReadModel\Repository
{
    public function flush(): void;

    public function flushWithContext(array $context): void;

    public function clear(): void;

    public function pendingOperations(): array;
}

In deferred mode, find($id) checks an in-memory identity map before DynamoDB. save() captures the read model's serialized state at the time save() is called and stages that state in memory. Repeated saves for the same read-model id replace the staged state and collapse to one PutItem on flush(). Mutating a read model after save() does not change the staged write unless save() is called again. remove() marks the id removed in memory, so find($id) returns null until the item is saved again or clear() discards the deferred state. Saving a removed id cancels the pending delete.

pendingOperations() returns the currently staged removes and save-time snapshots for inspection. flush() writes pending deletes and saves one item at a time through the same DynamoDB storage path as the immediate repository. If a write fails, the failing and remaining pending state is retained for retry or inspection. Successfully flushed entries are no longer pending. clear() only discards local managed, dirty, and removed state; it does not write anything.

Failures are reported with DeferredFlushFailed, which includes the operation, read-model id, table, repository name, read-model class, and previous exception. The repository does not know application-level details such as tenant id, projector name, or source event id. Pass those at the flush boundary when they are useful:

$repository->flushWithContext([
    'tenantId' => $tenantId,
    'projector' => SomeProjector::class,
    'sourceEventId' => $eventId,
]);

findAll() and findBy() are merge-aware: they query DynamoDB and overlay pending local saves/removes before returning results. They still perform a DynamoDB query each time, and DynamoDB reads use the repository's normal consistency settings. Deferred mode makes this repository's staged changes visible; it does not make broad projection queries transactional or globally fresh.

findBy() remains available for Broadway compatibility and small read-side collections. Do not use it as a write-side invariant or duplicate guard during command handling, seeding, or projection rebuilds. Model those cases as deterministic lookup read models and query them with find($id).

The identity map is scoped to the deferred repository instance. It can return a cached model even if another process changes DynamoDB while the deferred repository is still open. Keep deferred repositories short-lived around a replay, seed, or console unit of work, and call clear() before reusing one across independent work. For long replays or seeds, use explicit flush(); clear(); checkpoints when managed objects are no longer needed.

This is a batching and performance boundary, not a DynamoDB transaction. A failed flush can leave earlier operations persisted and later operations still pending.

Example AWS CLI setup:

aws dynamodb create-table \
  --table-name read-models \
  --billing-mode PAY_PER_REQUEST \
  --attribute-definitions \
    AttributeName=Name,AttributeType=S \
    AttributeName=Id,AttributeType=S \
  --key-schema \
    AttributeName=Name,KeyType=HASH \
    AttributeName=Id,KeyType=RANGE

Testing

The test suite expects DynamoDB Local. In GitHub Actions this is provided as a service named dynamodb-local; locally, override the endpoint:

DYNAMODB_ENDPOINT=http://127.0.0.1:8000 \
AWS_ACCESS_KEY_ID=none \
AWS_SECRET_ACCESS_KEY=none \
composer run-script phpunit

CI tests supported PHP versions against each supported AsyncAws DynamoDB major.

Quality Checks

vendor/bin/ecs check --config ecs.php
composer run-script phpstan
composer run-script infection

chrisjenkinson/dynamo-db-read-model 适用场景与选型建议

chrisjenkinson/dynamo-db-read-model 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 4.29k 次下载、GitHub Stars 达 0, 最近一次更新时间为 2022 年 09 月 24 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 chrisjenkinson/dynamo-db-read-model 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 chrisjenkinson/dynamo-db-read-model 我们能提供哪些服务?
定制开发 / 二次开发

基于 chrisjenkinson/dynamo-db-read-model 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 4.29k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 12
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: GPL-3.0-or-later
  • 更新时间: 2022-09-24