DDD Simple REST API based on Symfony providing key-value store


Tests codecov Mutation testing badge Scrutinizer Code Quality Packagist PHP Version Support Packagist License Packagist Version

This application provides a simple API for storing your data in your key-value store with history.



  • PHP 7.4+ with pdo_mysql and redis extensions
  • Composer
  • MariaDB or MySQL server
  • Redis server
composer install # Install dependencies
bin/console doctrine:migrations:migrate -n

Note that you may need to update your DB and Redis credentials. Simply put DATABASE_URL=mysql://admin:admin@ in .env.local and put your credentials there.


Application offers both Web and CLI interfaces.


The easiest way to run the application is by installing Symfony CLI and run:

symfony serve

Your app should be available at http://localhost:8000.

Use provided Swagger UI documentation to browse the API.


Application offers two CLI commands:

bin/console app:object-store:store <key> <value> # Stores the value in object store
bin/console app:object-store:get <key> [-t TIMESTAMP] # Gets the value from object store at given time

Please note that value must me JSON-encoded and escaped for your terminal. For example, if you want to store a string value: test-value at key test-key, you have to use following command:

bin/console app:object-store:store test-key \"test-value\"

This is to enable you to store complex data as a value.

Storage adapters

Application allows you to easily swap between different implementations of storage drivers. You can easily create one by implementing App\Infrastructure\ObjectStorage\ObjectStorageAdapter interface and tagging your service with object_storage.adapter tag (see example in config/services.yaml file).

Currently implemented drivers are:

  • DoctrineObjectStorageAdapter - stores data in Database
  • RedisObjectStorageAdapter - stores data in Redis

Choosing the adapter

To change the adapter, simply set STORAGE_ADAPTER environment variable to redis or doctrine:

# /.env.local


DoctrineObjectStorageAdapter uses Doctrine ORM to store data as ObjectEntry entities.

The ObjectEntry entity is using VARCHAR(255) column type.

  • utf8mb4 character set for full Unicode support so you can use keys like 🧅 or 😃.
  • utf8mb4_bin collation ensures binary comparison of keys so KEY !== key and key !== kęy.
  • Timestamp is stored as TIMESTAMP in UTC


Because of MySQL index length limitations, an error may occur when storing bigger keys.


RedisObjectStorageAdapter uses Redis as storage using sorted sets. Internally, the sorted set keeps only the change reference as:

key SHA512(key):timestamp timestamp

Then, the actual value is stored in a hashmap as:

SHA512(key):timestamp value

Because sorted sets require values to be unique, this approach allows value1 -> value2 -> value1 transitions to be properly stored.


Since the implementation uses SHA512 as part of the key, some conflicts may occur.


All components are covered by either unit or feature tests.

Running tests is as simple as configuring .env.test.local variables to match your DB credentials and running:


Mutation tests

To make sure your tests actually have necessary assertions, CI is running mutation tests using Infection.

Make sure you have Xdebug or PCOV extension enabled as it is required to run:



This application is being automatically tested using GitHub Actions and deployed on Heroku.