Search by

adachsoft / task-contracts

Arkadiusz Adach

task-contracts

Package info

gitlab.com/a.adach/task-contracts

Issues

pkg:composer/adachsoft/task-contracts

Statistics

Installs: 104

Dependents: 4

Suggesters: 0

Stars: 0

2.0.0 2026-10-06 16:54 UTC

This package is auto-updated.

Last update: 2026-10-06 15:03:13 UTC


README

adachsoft/task-contracts provides PHP contracts, immutable DTOs, value objects, exceptions, and application use cases for task management. The package defines repository APIs without prescribing a storage engine.

Requirements

  • PHP 8.3 or newer
  • adachsoft/collection 3.x
  • psr/clock 1.x

Installation

composer require adachsoft/task-contracts

The repository also contains the optional in-memory implementation:

composer require adachsoft/task-memory

Package structure

The main package contains:

  • repository interfaces for tasks and comments;
  • immutable task and comment DTOs;
  • typed task and comment collections;
  • task status resolution contracts and enum values;
  • domain exceptions;
  • TaskService and CommentService application use cases;
  • TaskServiceFactory, the recommended entry point for creating use cases.

adachsoft/task-memory provides InMemoryTaskRepository and InMemoryCommentRepository implementations for applications and tests. Other storage implementations can implement the repository interfaces independently.

Repository contracts

TaskRepositoryInterface

AdachSoft\TaskContracts\Interface\TaskRepositoryInterface

The interface supports finding, listing, updating, and deleting tasks; traversing task trees; managing dependencies; counting child tasks; and the following version 2.0 operations:

  • nextIdentity() returns the next available identifier without reserving it;
  • add() atomically inserts a task and persists it with version 1;
  • save() updates an existing task using optimistic locking;
  • addDependency() and removeDependency() update the task version.

save() is no longer an upsert. It requires the DTO version to match the stored version and increments the version after a successful update. Repository reads return the current version and current dependencies.

CommentRepositoryInterface

AdachSoft\TaskContracts\Interface\CommentRepositoryInterface

The interface supports finding, listing, updating, and deleting comments, counting comments by task, and the version 2.0 operations:

  • nextIdentity(string $taskId) returns the next comment identifier without reserving it;
  • add() atomically inserts a comment with version 1;
  • save() updates a comment using optimistic locking.

Task statuses

TaskStatusEnum contains:

use AdachSoft\TaskContracts\Enum\TaskStatusEnum;

TaskStatusEnum::TODO;
TaskStatusEnum::IN_PROGRESS;
TaskStatusEnum::DONE;
TaskStatusEnum::BLOCKED;
TaskStatusEnum::CANCELLED;

DTOs

TaskDto is a readonly object containing task identity, hierarchy, description, timestamps, status, dependencies, subtasks, and an optimistic-lock version.

CommentDto is a readonly object containing comment identity, task association, author, content, timestamps, and an optimistic-lock version.

The use-case DTOs are:

  • CreateTaskDto for task creation;
  • UpdateTaskDto for partial task updates, including explicit root-task reassignment;
  • TaskDetailsDto for a task together with comment and subtask counts.

Application use cases

Create services through TaskServiceFactory:

use AdachSoft\TaskContracts\UseCase\TaskServiceFactory;

$taskService = TaskServiceFactory::createTaskService(
    taskRepository: $taskRepository,
    commentRepository: $commentRepository,
);

$commentService = TaskServiceFactory::createCommentService(
    commentRepository: $commentRepository,
    taskRepository: $taskRepository,
);

TaskService provides task creation, updates, status changes, deletion, task reads, tree queries, dependency management, and task listing. CommentService provides comment creation, editing, deletion, lookup, and listing by task.

The factory accepts an optional Psr\Clock\ClockInterface and configurable validation limits. When no clock is supplied, the package uses the system clock.

Generated task and comment identifiers are retried only when an atomic insert detects a collision. Concurrent modifications are never retried automatically; callers should reload the DTO and decide how to reconcile the change.

Exceptions

The package exposes dedicated exceptions for common failure cases:

  • TaskAlreadyExistsException and CommentAlreadyExistsException for atomic insert collisions;
  • ConcurrentModificationException for optimistic-lock version conflicts;
  • InvalidTaskDataException for invalid identifiers and field values;
  • existing not-found exceptions for missing tasks and comments.

Example integration

Inject repository implementations into an application service or create the package use cases through the factory:

use AdachSoft\TaskContracts\Interface\TaskRepositoryInterface;
use AdachSoft\TaskContracts\Dto\TaskDto;

final class TaskReader
{
    public function __construct(
        private TaskRepositoryInterface $tasks,
    ) {
    }

    public function findTask(string $taskId): ?TaskDto
    {
        return $this->tasks->findById($taskId);
    }
}

The concrete repository is intentionally not part of the main package. Select an implementation that matches the application environment and inject it through the interface.

Concurrency model

Repository writes use optimistic locking. add() assigns version 1; successful save(), addDependency(), and removeDependency() operations increment the stored version. A stale DTO causes ConcurrentModificationException instead of silently overwriting another client's changes.

See docs/adr/0001-task-storage-concurrency.md for the storage and concurrency decision record.

Development

Install development dependencies and run the quality checks:

composer install
vendor/bin/phpunit
vendor/bin/phpstan analyse src
vendor/bin/phpstan analyse tests
vendor/bin/rector process src
vendor/bin/rector process tests
vendor/bin/php-cs-fixer fix src
vendor/bin/php-cs-fixer fix tests

License

This library is released under the MIT License. See the LICENSE file for details.