From 52d2ae3729001d66551d327d1c76dc976ef6f6e1 Mon Sep 17 00:00:00 2001
From: Marko Ivancic <marko.ivancic@srce.hr>
Date: Mon, 8 May 2023 11:51:52 +0000
Subject: [PATCH] Refactor trackers

---
 README.md                                     |  50 +-
 composer.lock                                 | 132 ++-
 config-templates/module_accounting.php        |  86 +-
 hooks/hook_cron.php                           |  19 +-
 routing/routes/routes.yml                     |   9 -
 src/Auth/Process/Accounting.php               |  39 +-
 .../DoctrineDbal/VersionedDataProvider.php    |  95 ++
 .../Builders/DataProviderBuilder.php          | 105 +++
 .../DoctrineDbal/CurrentDataProvider.php      |  95 ++
 .../DoctrineDbal/VersionedDataProvider.php    |  95 ++
 .../Interfaces/ActivityInterface.php}         |   8 +-
 .../Interfaces/ConnectedServicesInterface.php |  20 +
 .../Interfaces/DataProviderInterface.php      |  22 +
 .../Activity/DoctrineDbal/Versioned/Store.php | 146 +++
 .../Version20220801000000CreateIdpTable.php   |  11 +
 ...ion20220801000100CreateIdpVersionTable.php |  11 +
 .../Version20220801000200CreateSpTable.php    |  11 +
 ...sion20220801000300CreateSpVersionTable.php |  11 +
 .../Version20220801000400CreateUserTable.php  |  11 +
 ...on20220801000500CreateUserVersionTable.php |  11 +
 ...20801000600CreateIdpSpUserVersionTable.php |  11 +
 ...01000700CreateAuthenticationEventTable.php |   6 +-
 .../Versioned/Store/RawActivity.php           |   4 +-
 .../Versioned/Store/Repository.php            | 243 +++++
 .../Versioned/Store/TableConstants.php        |  30 +
 .../Bases/DoctrineDbal/Current/Store.php      | 242 +++++
 .../Store/Migrations/CreateIdpTable.php       |  87 ++
 .../Store/Migrations/CreateSpTable.php        |  87 ++
 .../Store/Migrations/CreateUserTable.php      |  78 ++
 .../Migrations/CreateUserVersionTable.php     |  15 +
 .../DoctrineDbal/Current/Store/Repository.php | 530 +++++++++++
 .../Current/Store/TableConstants.php          |  52 ++
 .../Bases}/DoctrineDbal/Versioned/Store.php   | 165 +---
 .../CreateIdpSpUserVersionTable.php}          |  10 +-
 .../Store/Migrations/CreateIdpTable.php}      |  12 +-
 .../Migrations/CreateIdpVersionTable.php}     |  12 +-
 .../Store/Migrations/CreateSpTable.php}       |  12 +-
 .../Migrations/CreateSpVersionTable.php}      |  12 +-
 .../Store/Migrations/CreateUserTable.php}     |  12 +-
 .../Migrations/CreateUserVersionTable.php}    |  12 +-
 .../Versioned/Store/Repository.php            | 469 +---------
 .../Versioned/Store/TableConstants.php        |  43 +-
 .../Accounting/Bases}/HashDecoratedState.php  |   2 +-
 .../Accounting/Bases/TableConstants.php       |  15 +
 .../DoctrineDbal/Current/Store.php            | 154 ++++
 .../Version20240505100CreateSpTable.php       |  11 +
 .../Version20240505200CreateUserTable.php     |  11 +
 ...rsion20240505300CreateUserVersionTable.php |  11 +
 ...20240505400CreateConnectedServiceTable.php | 104 +++
 .../DoctrineDbal/Current/Store/Repository.php | 332 +++++++
 .../Current/Store/TableConstants.php          |  33 +
 .../DoctrineDbal/RawConnectedService.php}     |  47 +-
 .../DoctrineDbal/Versioned/Store.php          | 154 ++++
 .../Version20220801000000CreateIdpTable.php   |  11 +
 ...ion20220801000100CreateIdpVersionTable.php |  11 +
 .../Version20220801000200CreateSpTable.php    |  11 +
 ...sion20220801000300CreateSpVersionTable.php |  11 +
 .../Version20220801000400CreateUserTable.php  |  11 +
 ...on20220801000500CreateUserVersionTable.php |  11 +
 ...20801000600CreateIdpSpUserVersionTable.php |  11 +
 ...20801000700CreateConnectedServiceTable.php |  86 ++
 .../Versioned/Store/Repository.php            | 612 +++++++++++++
 .../Versioned/Store/TableConstants.php        |  30 +
 src/{ => Data}/Stores/Bases/AbstractStore.php |   2 +-
 .../Bases/DoctrineDbal/AbstractRawEntity.php  |   2 +-
 .../Bases/DoctrineDbal/AbstractStore.php      |  12 +-
 .../Builders/Bases/AbstractStoreBuilder.php   |   8 +-
 .../Stores/Builders/DataStoreBuilder.php      |   7 +-
 .../Stores/Builders/JobsStoreBuilder.php      |   6 +-
 .../Connections/Bases/AbstractMigrator.php    |   4 +-
 .../DoctrineDbal/Bases/AbstractMigration.php  |   6 +-
 .../Connections/DoctrineDbal/Connection.php   |   4 +-
 .../Connections/DoctrineDbal/Factory.php      |   2 +-
 .../Connections/DoctrineDbal/Migrator.php     |   8 +-
 .../Stores/Interfaces/ActivityInterface.php   |  12 +
 .../Interfaces/ConnectedServicesInterface.php |  12 +
 .../Stores/Interfaces/ConnectionInterface.php |   2 +-
 .../Stores/Interfaces/DataStoreInterface.php  |   8 +-
 .../Stores/Interfaces/JobsStoreInterface.php  |   2 +-
 .../Stores/Interfaces/MigrationInterface.php  |   2 +-
 .../Stores/Interfaces/StoreInterface.php      |   2 +-
 .../Stores/Jobs/DoctrineDbal/Store.php        |  12 +-
 .../Bases/AbstractCreateJobsTable.php         |   6 +-
 .../Version20220601000000CreateJobTable.php   |   4 +-
 ...sion20220601000100CreateJobFailedTable.php |  15 +
 .../Stores/Jobs/DoctrineDbal/Store/RawJob.php |  38 +-
 .../Jobs/DoctrineDbal/Store/Repository.php    |   6 +-
 .../DoctrineDbal/Store/TableConstants.php     |   2 +-
 .../Stores/Jobs/PhpRedis/RedisStore.php       |   6 +-
 .../DoctrineDbal/VersionedDataTracker.php     |  40 +
 .../Trackers/Builders/DataTrackerBuilder.php} |  18 +-
 .../DoctrineDbal/CurrentDataTracker.php       |  40 +
 .../DoctrineDbal/VersionedDataTracker.php     |  40 +
 .../Interfaces/DataTrackerInterface.php}      |   4 +-
 .../Authentication/Event/State/Oidc.php       |   4 +-
 ...rviceProvider.php => ConnectedService.php} |   4 +-
 .../Bag.php                                   |  12 +-
 src/Entities/Providers/Identity/Oidc.php      |   2 +-
 src/Entities/Providers/Service/Oidc.php       |   2 +-
 src/Http/Controllers/Admin/Configuration.php  |  40 +-
 src/Http/Controllers/User/Profile.php         | 185 ++--
 src/ModuleConfiguration.php                   |  66 +-
 .../AuthenticationDataProviderBuilder.php     |  64 --
 src/Services/JobRunner.php                    |  38 +-
 src/Services/TrackerResolver.php              |  66 ++
 ...sion20220601000100CreateJobFailedTable.php |  15 -
 .../DoctrineDbal/Versioned/Tracker.php        | 104 ---
 templates/admin/configuration/status.twig     |  59 +-
 templates/user/activity.twig                  |   2 +-
 templates/user/connected-organizations.twig   |   4 +-
 templates/user/oidc-tokens.twig               |  78 --
 tests/config-templates/config.php             |   4 +-
 tests/config-templates/module_accounting.php  |  21 +-
 tests/src/Auth/Process/AccountingTest.php     |  54 +-
 tests/src/Constants/RawRowResult.php          |  25 +-
 .../Builders/DataProviderBuilderTest.php      |  90 ++
 ...0700CreateAuthenticationEventTableTest.php |  14 +-
 .../Versioned/Store/RawActivityTest.php       |  17 +-
 .../Versioned/Store/RepositoryTest.php        | 328 +++++++
 .../DoctrineDbal/Versioned/StoreTest.php      | 416 +++++++++
 .../CreateIdpSpUserVersionTableTest.php       | 103 +++
 .../Store/Migrations/CreateIdpTableTest.php}  |  24 +-
 .../Migrations/CreateIdpVersionTableTest.php} |  24 +-
 .../Store/Migrations/CreateSpTableTest.php}   |  24 +-
 .../Migrations/CreateSpVersionTableTest.php}  |  24 +-
 .../Store/Migrations/CreateUserTableTest.php} |  25 +-
 .../CreateUserVersionTableTest.php}           |  24 +-
 .../Versioned/Store/RepositoryTest.php        | 394 ++++++++
 .../DoctrineDbal/Versioned/StoreTest.php      | 372 ++------
 .../Bases}/HashDecoratedStateTest.php         |   8 +-
 .../DoctrineDbal/RawConnectedServiceTest.php} |  65 +-
 .../Versioned/Store/RepositoryTest.php        | 383 ++++++++
 .../DoctrineDbal/Versioned/StoreTest.php      | 257 ++++++
 .../Stores/Bases/AbstractStoreTest.php        |  16 +-
 .../DoctrineDbal/AbstractRawEntityTest.php    |   8 +-
 .../Stores/Builders/DataStoreBuilderTest.php  |  31 +-
 .../Stores/Builders/JobsStoreBuilderTest.php  |  30 +-
 .../Bases/AbstractMigratorTest.php            |  44 +-
 .../Bases/AbstractMigrationTest.php           |  16 +-
 .../DoctrineDbal/ConnectionTest.php           |   8 +-
 .../Connections/DoctrineDbal/FactoryTest.php  |  18 +-
 .../Connections/DoctrineDbal/MigratorTest.php |  49 +-
 ...ersion20220601000000CreateJobTableTest.php |  22 +-
 ...20220601000100CreateJobFailedTableTest.php |  22 +-
 .../Jobs/DoctrineDbal/Store/RawJobTest.php    |  44 +-
 .../DoctrineDbal/Store/RepositoryTest.php     |  47 +-
 .../Stores/Jobs/DoctrineDbal/StoreTest.php    |  52 +-
 .../Stores/Jobs/PhpRedis/RedisStoreTest.php   |  28 +-
 .../Versioned/DataTrackerTest.php}            | 122 +--
 .../Builders/DataTrackerBuilderTest.php}      |  23 +-
 .../Versioned/DataTrackerTest.php             | 199 ++++
 .../BagTest.php                               |  10 +-
 ...viderTest.php => ConnectedServiceTest.php} |   8 +-
 tests/src/ModuleConfigurationTest.php         |  21 +-
 .../AuthenticationDataProviderBuilderTest.php |  87 --
 tests/src/Services/JobRunnerTest.php          | 105 ++-
 ...1000600CreateIdpSpUserVersionTableTest.php | 107 ---
 .../Versioned/Store/RepositoryTest.php        | 849 ------------------
 158 files changed, 7237 insertions(+), 3312 deletions(-)
 create mode 100644 src/Data/Providers/Activity/DoctrineDbal/VersionedDataProvider.php
 create mode 100644 src/Data/Providers/Builders/DataProviderBuilder.php
 create mode 100644 src/Data/Providers/ConnectedServices/DoctrineDbal/CurrentDataProvider.php
 create mode 100644 src/Data/Providers/ConnectedServices/DoctrineDbal/VersionedDataProvider.php
 rename src/{Providers/Interfaces/AuthenticationDataProviderInterface.php => Data/Providers/Interfaces/ActivityInterface.php} (63%)
 create mode 100644 src/Data/Providers/Interfaces/ConnectedServicesInterface.php
 create mode 100644 src/Data/Providers/Interfaces/DataProviderInterface.php
 create mode 100644 src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store.php
 create mode 100644 src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTable.php
 create mode 100644 src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTable.php
 create mode 100644 src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTable.php
 create mode 100644 src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTable.php
 create mode 100644 src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTable.php
 create mode 100644 src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTable.php
 create mode 100644 src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTable.php
 rename src/{Stores/Data/Authentication => Data/Stores/Accounting/Activity}/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php (88%)
 rename src/{Stores/Data/Authentication => Data/Stores/Accounting/Activity}/DoctrineDbal/Versioned/Store/RawActivity.php (96%)
 create mode 100644 src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Repository.php
 create mode 100644 src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/TableConstants.php
 create mode 100644 src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store.php
 create mode 100644 src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateIdpTable.php
 create mode 100644 src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateSpTable.php
 create mode 100644 src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserTable.php
 create mode 100644 src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserVersionTable.php
 create mode 100644 src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Repository.php
 create mode 100644 src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/TableConstants.php
 rename src/{Stores/Data/Authentication => Data/Stores/Accounting/Bases}/DoctrineDbal/Versioned/Store.php (70%)
 rename src/{Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTable.php => Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpSpUserVersionTable.php} (87%)
 rename src/{Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTable.php => Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpTable.php} (80%)
 rename src/{Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTable.php => Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpVersionTable.php} (80%)
 rename src/{Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTable.php => Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpTable.php} (80%)
 rename src/{Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTable.php => Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpVersionTable.php} (81%)
 rename src/{Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTable.php => Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserTable.php} (79%)
 rename src/{Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTable.php => Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserVersionTable.php} (81%)
 rename src/{Stores/Data/Authentication => Data/Stores/Accounting/Bases}/DoctrineDbal/Versioned/Store/Repository.php (54%)
 rename src/{Stores/Data/Authentication => Data/Stores/Accounting/Bases}/DoctrineDbal/Versioned/Store/TableConstants.php (61%)
 rename src/{Stores/Data/Authentication/DoctrineDbal/Versioned/Store => Data/Stores/Accounting/Bases}/HashDecoratedState.php (96%)
 create mode 100644 src/Data/Stores/Accounting/Bases/TableConstants.php
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store.php
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505100CreateSpTable.php
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505200CreateUserTable.php
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505300CreateUserVersionTable.php
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505400CreateConnectedServiceTable.php
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Repository.php
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/TableConstants.php
 rename src/{Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawConnectedServiceProvider.php => Data/Stores/Accounting/ConnectedServices/DoctrineDbal/RawConnectedService.php} (68%)
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store.php
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTable.php
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTable.php
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTable.php
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTable.php
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTable.php
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTable.php
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTable.php
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateConnectedServiceTable.php
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Repository.php
 create mode 100644 src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/TableConstants.php
 rename src/{ => Data}/Stores/Bases/AbstractStore.php (97%)
 rename src/{ => Data}/Stores/Bases/DoctrineDbal/AbstractRawEntity.php (95%)
 rename src/{ => Data}/Stores/Bases/DoctrineDbal/AbstractStore.php (88%)
 rename src/{ => Data}/Stores/Builders/Bases/AbstractStoreBuilder.php (89%)
 rename src/{ => Data}/Stores/Builders/DataStoreBuilder.php (74%)
 rename src/{ => Data}/Stores/Builders/JobsStoreBuilder.php (78%)
 rename src/{ => Data}/Stores/Connections/Bases/AbstractMigrator.php (96%)
 rename src/{ => Data}/Stores/Connections/DoctrineDbal/Bases/AbstractMigration.php (89%)
 rename src/{ => Data}/Stores/Connections/DoctrineDbal/Connection.php (91%)
 rename src/{ => Data}/Stores/Connections/DoctrineDbal/Factory.php (92%)
 rename src/{ => Data}/Stores/Connections/DoctrineDbal/Migrator.php (94%)
 create mode 100644 src/Data/Stores/Interfaces/ActivityInterface.php
 create mode 100644 src/Data/Stores/Interfaces/ConnectedServicesInterface.php
 rename src/{ => Data}/Stores/Interfaces/ConnectionInterface.php (51%)
 rename src/{ => Data}/Stores/Interfaces/DataStoreInterface.php (61%)
 rename src/{ => Data}/Stores/Interfaces/JobsStoreInterface.php (93%)
 rename src/{ => Data}/Stores/Interfaces/MigrationInterface.php (81%)
 rename src/{ => Data}/Stores/Interfaces/StoreInterface.php (89%)
 rename src/{ => Data}/Stores/Jobs/DoctrineDbal/Store.php (91%)
 rename src/{ => Data}/Stores/Jobs/DoctrineDbal/Store/Migrations/Bases/AbstractCreateJobsTable.php (86%)
 rename src/{ => Data}/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTable.php (58%)
 create mode 100644 src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTable.php
 rename src/{ => Data}/Stores/Jobs/DoctrineDbal/Store/RawJob.php (60%)
 rename src/{ => Data}/Stores/Jobs/DoctrineDbal/Store/Repository.php (96%)
 rename src/{ => Data}/Stores/Jobs/DoctrineDbal/Store/TableConstants.php (84%)
 rename src/{ => Data}/Stores/Jobs/PhpRedis/RedisStore.php (96%)
 create mode 100644 src/Data/Trackers/Activity/DoctrineDbal/VersionedDataTracker.php
 rename src/{Trackers/Builders/AuthenticationDataTrackerBuilder.php => Data/Trackers/Builders/DataTrackerBuilder.php} (71%)
 create mode 100644 src/Data/Trackers/ConnectedServices/DoctrineDbal/CurrentDataTracker.php
 create mode 100644 src/Data/Trackers/ConnectedServices/DoctrineDbal/VersionedDataTracker.php
 rename src/{Trackers/Interfaces/AuthenticationDataTrackerInterface.php => Data/Trackers/Interfaces/DataTrackerInterface.php} (77%)
 rename src/Entities/{ConnectedServiceProvider.php => ConnectedService.php} (94%)
 rename src/Entities/{ConnectedServiceProvider => ConnectedService}/Bag.php (53%)
 delete mode 100644 src/Providers/Builders/AuthenticationDataProviderBuilder.php
 create mode 100644 src/Services/TrackerResolver.php
 delete mode 100644 src/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTable.php
 delete mode 100644 src/Trackers/Authentication/DoctrineDbal/Versioned/Tracker.php
 delete mode 100644 templates/user/oidc-tokens.twig
 create mode 100644 tests/src/Data/Providers/Builders/DataProviderBuilderTest.php
 rename tests/src/{Stores/Data/Authentication => Data/Stores/Accounting/Activity}/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTableTest.php (84%)
 rename tests/src/{Stores/Data/Authentication => Data/Stores/Accounting/Activity}/DoctrineDbal/Versioned/Store/RawActivityTest.php (89%)
 create mode 100644 tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RepositoryTest.php
 create mode 100644 tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/StoreTest.php
 create mode 100644 tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpSpUserVersionTableTest.php
 rename tests/src/{Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTableTest.php => Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpTableTest.php} (74%)
 rename tests/src/{Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTableTest.php => Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpVersionTableTest.php} (73%)
 rename tests/src/{Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTableTest.php => Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpTableTest.php} (74%)
 rename tests/src/{Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTableTest.php => Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpVersionTableTest.php} (73%)
 rename tests/src/{Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTableTest.php => Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserTableTest.php} (73%)
 rename tests/src/{Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTableTest.php => Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserVersionTableTest.php} (73%)
 create mode 100644 tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/RepositoryTest.php
 rename tests/src/{Stores/Data/Authentication => Data/Stores/Accounting/Bases}/DoctrineDbal/Versioned/StoreTest.php (56%)
 rename tests/src/{Stores/Data/Authentication/DoctrineDbal/Versioned/Store => Data/Stores/Accounting/Bases}/HashDecoratedStateTest.php (88%)
 rename tests/src/{Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawConnectedServiceProviderTest.php => Data/Stores/Accounting/ConnectedServices/DoctrineDbal/RawConnectedServiceTest.php} (60%)
 create mode 100644 tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/RepositoryTest.php
 create mode 100644 tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/StoreTest.php
 rename tests/src/{ => Data}/Stores/Bases/AbstractStoreTest.php (72%)
 rename tests/src/{ => Data}/Stores/Bases/DoctrineDbal/AbstractRawEntityTest.php (89%)
 rename tests/src/{ => Data}/Stores/Builders/DataStoreBuilderTest.php (55%)
 rename tests/src/{ => Data}/Stores/Builders/JobsStoreBuilderTest.php (77%)
 rename tests/src/{ => Data}/Stores/Connections/Bases/AbstractMigratorTest.php (76%)
 rename tests/src/{ => Data}/Stores/Connections/DoctrineDbal/Bases/AbstractMigrationTest.php (83%)
 rename tests/src/{ => Data}/Stores/Connections/DoctrineDbal/ConnectionTest.php (79%)
 rename tests/src/{ => Data}/Stores/Connections/DoctrineDbal/FactoryTest.php (69%)
 rename tests/src/{ => Data}/Stores/Connections/DoctrineDbal/MigratorTest.php (82%)
 rename tests/src/{ => Data}/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTableTest.php (77%)
 rename tests/src/{ => Data}/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTableTest.php (78%)
 rename tests/src/{ => Data}/Stores/Jobs/DoctrineDbal/Store/RawJobTest.php (66%)
 rename tests/src/{ => Data}/Stores/Jobs/DoctrineDbal/Store/RepositoryTest.php (81%)
 rename tests/src/{ => Data}/Stores/Jobs/DoctrineDbal/StoreTest.php (86%)
 rename tests/src/{ => Data}/Stores/Jobs/PhpRedis/RedisStoreTest.php (90%)
 rename tests/src/{Trackers/Authentication/DoctrineDbal/Versioned/TrackerTest.php => Data/Trackers/Activity/DoctrineDbal/Versioned/DataTrackerTest.php} (52%)
 rename tests/src/{Trackers/Builders/AuthenticationDataTrackerBuilderTest.php => Data/Trackers/Builders/DataTrackerBuilderTest.php} (77%)
 create mode 100644 tests/src/Data/Trackers/ConnectedServices/DoctrineDbal/Versioned/DataTrackerTest.php
 rename tests/src/Entities/{ConnectedServiceProvider => ConnectedService}/BagTest.php (73%)
 rename tests/src/Entities/{ConnectedServiceProviderTest.php => ConnectedServiceTest.php} (89%)
 delete mode 100644 tests/src/Providers/Builders/AuthenticationDataProviderBuilderTest.php
 delete mode 100644 tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTableTest.php
 delete mode 100644 tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RepositoryTest.php

diff --git a/README.md b/README.md
index 7061c71..4856b53 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,10 @@
 [![Test](https://github.com/cicnavi/simplesamlphp-module-accounting/actions/workflows/test.yml/badge.svg)](https://github.com/cicnavi/simplesamlphp-module-accounting/actions/workflows/test.yml)
 
 
-* TODO 
-* Consider config to enable / disable storing attribute values
-* IG Binary - consider using for data serialization
-
 
 # simplesamlphp-module-accounting
-SimpleSAMLphp module providing user accounting functionality using SimpleSAMLphp authentication processing 
-filters feature.
+SimpleSAMLphp module providing user "Profile Page" and accounting functionality using SimpleSAMLphp authentication
+processing filters feature.
 
 ## Features
 - Enables tracking of authentication events, synchronously (during authentication event) or
@@ -21,10 +17,9 @@ backend storages can be added by following proper interfaces.
 - Comes with setup procedure which sets up backend storage. In case of Doctrine DBAL this means running SQL migrations
 which create proper tables in configured database.
 - Each backend storage connection can have master and slave configuration (master for writing, slave for reading)
-- Has "trackers" which persist authentication data to backend storage. Currently, there is one default Doctrine DBAL
-compatible tracker which stores authentication events, versioned Idp and SP metadata, and versioned user attributes.
-Other trackers can be added by following proper interfaces.
-- Trackers can run in two ways:
+- Has tracking functionality available which persist authentication data to backend storage. Currently, module can
+track connected services and authentication events. Other trackers can be added by following proper interfaces.
+- Tracking can run in two ways:
   - synchronously - authentication data persisted during authentication event typically with multiple
   queries / inserts / updates to backend storage.
   - asynchronously - only authentication event job is persisted during authentication event
@@ -62,24 +57,33 @@ to the SimpleSAMLphp config directory:
 cp modules/accounting/config-templates/module_accounting.php config/
 ```
 
-Next step is configuring available options in file config/module_accounting.php. Each option has an explanation,
+Next step is to configure available options in file config/module_accounting.php. Each option has an explanation,
 however, the description of the overall concept follows.
 
-For accounting processing, the default data tracker and data provider class must be set. This tracker will be used
-to persist tracking data and also to show data in the SimpleSAMLphp user interface. Here is an example excerpt
-of setting the Doctrine DBAL compatible tracker class which will store authentication events, versioned Idp
-and SP metadata, and versioned user attributes in a relational database:
+Module can be configured to only show current user data, with no accounting taking place. However, module can be
+configured to track the following data:
+* Connected organizations - by setting the class ModuleConfiguration::OPTION_PROVIDER_FOR_CONNECTED_SERVICES option. 
+* Activity - by setting the class for ModuleConfiguration::OPTION_PROVIDER_FOR_ACTIVITY option.
+
+Module comes with some Doctrine DBAL capable classes which can be used for those purposes. Here is an example config
+excerpt which will enable storing current (latest) data for connected services and versioned data 
+for authentication events, including versioned Idp and SP metadata, and versioned user attributes:
 
 ```php
 use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Trackers;
+use SimpleSAML\Module\accounting\Data\Trackers;
+use SimpleSAML\Module\accounting\Data\Providers;
 
 // ...
-ModuleConfiguration::OPTION_DEFAULT_DATA_TRACKER_AND_PROVIDER =>
-    Trackers\Authentication\DoctrineDbal\Versioned\Tracker::class,
+ModuleConfiguration::OPTION_PROVIDER_FOR_CONNECTED_SERVICES =>
+    Providers\ConnectedServices\DoctrineDbal\CurrentDataProvider::class,
+ModuleConfiguration::OPTION_PROVIDER_FOR_ACTIVITY =>
+        Providers\Activity\DoctrineDbal\VersionedDataProvider::class,
 // ...
 ```
 
+### Processing type
+
 The deployer can choose if the accounting processing will be performed during authentication event (synchronously),
 or in a separate process (asynchronously), for example:
 
@@ -145,8 +149,14 @@ Only one job runner instance can run at given point in time. By maintaining inte
 if there is another job runner active. If yes, the latter will simply exit and let the active job runner do its work.
 This way one is free to invoke the cron tag at any time, since only one job runner will ever be active.
 
-## TODO
-- [ ] Translation
+## OpendID Connect integration
+This module can also be used as an authentication processing filter for OIDC module 
+https://github.com/simplesamlphp/simplesamlphp-module-oidc, meaning it can also track OIDC authentication events, 
+Also, if connected services option is enabled, a user will be able to revoke any active access / refresh tokens 
+for particular service in the user interface.
+
+Accounting authentication processing filter can be added in the OIDC module configuration, as per OIDC module
+documentation.
 
 ## Tests
 To run phpcs, psalm and phpunit:
diff --git a/composer.lock b/composer.lock
index 76ddc7a..49f607c 100644
--- a/composer.lock
+++ b/composer.lock
@@ -154,16 +154,16 @@
         },
         {
             "name": "doctrine/dbal",
-            "version": "3.6.1",
+            "version": "3.6.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/dbal.git",
-                "reference": "57815c7bbcda3cd18871d253c1dd8cbe56f8526e"
+                "reference": "b4bd1cfbd2b916951696d82e57d054394d84864c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/dbal/zipball/57815c7bbcda3cd18871d253c1dd8cbe56f8526e",
-                "reference": "57815c7bbcda3cd18871d253c1dd8cbe56f8526e",
+                "url": "https://api.github.com/repos/doctrine/dbal/zipball/b4bd1cfbd2b916951696d82e57d054394d84864c",
+                "reference": "b4bd1cfbd2b916951696d82e57d054394d84864c",
                 "shasum": ""
             },
             "require": {
@@ -179,9 +179,9 @@
                 "doctrine/coding-standard": "11.1.0",
                 "fig/log-test": "^1",
                 "jetbrains/phpstorm-stubs": "2022.3",
-                "phpstan/phpstan": "1.10.3",
+                "phpstan/phpstan": "1.10.9",
                 "phpstan/phpstan-strict-rules": "^1.5",
-                "phpunit/phpunit": "9.6.4",
+                "phpunit/phpunit": "9.6.6",
                 "psalm/plugin-phpunit": "0.18.4",
                 "squizlabs/php_codesniffer": "3.7.2",
                 "symfony/cache": "^5.4|^6.0",
@@ -246,7 +246,7 @@
             ],
             "support": {
                 "issues": "https://github.com/doctrine/dbal/issues",
-                "source": "https://github.com/doctrine/dbal/tree/3.6.1"
+                "source": "https://github.com/doctrine/dbal/tree/3.6.2"
             },
             "funding": [
                 {
@@ -262,7 +262,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-03-02T19:26:24+00:00"
+            "time": "2023-04-14T07:25:38+00:00"
         },
         {
             "name": "doctrine/deprecations",
@@ -2196,22 +2196,22 @@
         },
         {
             "name": "guzzlehttp/guzzle",
-            "version": "7.5.0",
+            "version": "7.5.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba"
+                "reference": "b964ca597e86b752cd994f27293e9fa6b6a95ed9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba",
-                "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b964ca597e86b752cd994f27293e9fa6b6a95ed9",
+                "reference": "b964ca597e86b752cd994f27293e9fa6b6a95ed9",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
                 "guzzlehttp/promises": "^1.5",
-                "guzzlehttp/psr7": "^1.9 || ^2.4",
+                "guzzlehttp/psr7": "^1.9.1 || ^2.4.5",
                 "php": "^7.2.5 || ^8.0",
                 "psr/http-client": "^1.0",
                 "symfony/deprecation-contracts": "^2.2 || ^3.0"
@@ -2304,7 +2304,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/guzzle/issues",
-                "source": "https://github.com/guzzle/guzzle/tree/7.5.0"
+                "source": "https://github.com/guzzle/guzzle/tree/7.5.1"
             },
             "funding": [
                 {
@@ -2320,7 +2320,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-08-28T15:39:27+00:00"
+            "time": "2023-04-17T16:30:08+00:00"
         },
         {
             "name": "guzzlehttp/promises",
@@ -2408,22 +2408,22 @@
         },
         {
             "name": "guzzlehttp/psr7",
-            "version": "2.4.4",
+            "version": "2.5.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/psr7.git",
-                "reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf"
+                "reference": "b635f279edd83fc275f822a1188157ffea568ff6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/psr7/zipball/3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf",
-                "reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/b635f279edd83fc275f822a1188157ffea568ff6",
+                "reference": "b635f279edd83fc275f822a1188157ffea568ff6",
                 "shasum": ""
             },
             "require": {
                 "php": "^7.2.5 || ^8.0",
                 "psr/http-factory": "^1.0",
-                "psr/http-message": "^1.0",
+                "psr/http-message": "^1.1 || ^2.0",
                 "ralouphie/getallheaders": "^3.0"
             },
             "provide": {
@@ -2443,9 +2443,6 @@
                 "bamarni-bin": {
                     "bin-links": true,
                     "forward-command": false
-                },
-                "branch-alias": {
-                    "dev-master": "2.4-dev"
                 }
             },
             "autoload": {
@@ -2507,7 +2504,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/psr7/issues",
-                "source": "https://github.com/guzzle/psr7/tree/2.4.4"
+                "source": "https://github.com/guzzle/psr7/tree/2.5.0"
             },
             "funding": [
                 {
@@ -2523,7 +2520,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-03-09T13:19:02+00:00"
+            "time": "2023-04-17T16:11:26+00:00"
         },
         {
             "name": "justinrainbow/json-schema",
@@ -3333,16 +3330,16 @@
         },
         {
             "name": "netresearch/jsonmapper",
-            "version": "v4.1.0",
+            "version": "v4.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/cweiske/jsonmapper.git",
-                "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f"
+                "reference": "f60565f8c0566a31acf06884cdaa591867ecc956"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f",
-                "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f",
+                "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/f60565f8c0566a31acf06884cdaa591867ecc956",
+                "reference": "f60565f8c0566a31acf06884cdaa591867ecc956",
                 "shasum": ""
             },
             "require": {
@@ -3378,9 +3375,9 @@
             "support": {
                 "email": "cweiske@cweiske.de",
                 "issues": "https://github.com/cweiske/jsonmapper/issues",
-                "source": "https://github.com/cweiske/jsonmapper/tree/v4.1.0"
+                "source": "https://github.com/cweiske/jsonmapper/tree/v4.2.0"
             },
-            "time": "2022-12-08T20:46:14+00:00"
+            "time": "2023-04-09T17:37:40+00:00"
         },
         {
             "name": "nette/component-model",
@@ -4149,16 +4146,16 @@
         },
         {
             "name": "phpstan/phpdoc-parser",
-            "version": "1.17.1",
+            "version": "1.20.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpstan/phpdoc-parser.git",
-                "reference": "d3753fcb3abc6f78f5de6f72153d4b9c99c72dee"
+                "reference": "6c04009f6cae6eda2f040745b6b846080ef069c2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/d3753fcb3abc6f78f5de6f72153d4b9c99c72dee",
-                "reference": "d3753fcb3abc6f78f5de6f72153d4b9c99c72dee",
+                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6c04009f6cae6eda2f040745b6b846080ef069c2",
+                "reference": "6c04009f6cae6eda2f040745b6b846080ef069c2",
                 "shasum": ""
             },
             "require": {
@@ -4188,9 +4185,9 @@
             "description": "PHPDoc parser with support for nullable, intersection and generic types",
             "support": {
                 "issues": "https://github.com/phpstan/phpdoc-parser/issues",
-                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.17.1"
+                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.3"
             },
-            "time": "2023-04-04T11:11:22+00:00"
+            "time": "2023-04-25T09:01:03+00:00"
         },
         {
             "name": "phpunit/php-code-coverage",
@@ -4512,16 +4509,16 @@
         },
         {
             "name": "phpunit/phpunit",
-            "version": "9.6.6",
+            "version": "9.6.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "b65d59a059d3004a040c16a82e07bbdf6cfdd115"
+                "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b65d59a059d3004a040c16a82e07bbdf6cfdd115",
-                "reference": "b65d59a059d3004a040c16a82e07bbdf6cfdd115",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c993f0d3b0489ffc42ee2fe0bd645af1538a63b2",
+                "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2",
                 "shasum": ""
             },
             "require": {
@@ -4595,7 +4592,7 @@
             "support": {
                 "issues": "https://github.com/sebastianbergmann/phpunit/issues",
                 "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
-                "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.6"
+                "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.7"
             },
             "funding": [
                 {
@@ -4611,7 +4608,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-03-27T11:43:46+00:00"
+            "time": "2023-04-14T08:58:40+00:00"
         },
         {
             "name": "psr/container",
@@ -4713,21 +4710,21 @@
         },
         {
             "name": "psr/http-client",
-            "version": "1.0.1",
+            "version": "1.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/http-client.git",
-                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+                "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
-                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+                "url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31",
+                "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31",
                 "shasum": ""
             },
             "require": {
                 "php": "^7.0 || ^8.0",
-                "psr/http-message": "^1.0"
+                "psr/http-message": "^1.0 || ^2.0"
             },
             "type": "library",
             "extra": {
@@ -4747,7 +4744,7 @@
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
             "description": "Common interface for HTTP clients",
@@ -4759,27 +4756,27 @@
                 "psr-18"
             ],
             "support": {
-                "source": "https://github.com/php-fig/http-client/tree/master"
+                "source": "https://github.com/php-fig/http-client/tree/1.0.2"
             },
-            "time": "2020-06-29T06:28:15+00:00"
+            "time": "2023-04-10T20:12:12+00:00"
         },
         {
             "name": "psr/http-factory",
-            "version": "1.0.1",
+            "version": "1.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/http-factory.git",
-                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
+                "reference": "e616d01114759c4c489f93b099585439f795fe35"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
-                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
+                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
+                "reference": "e616d01114759c4c489f93b099585439f795fe35",
                 "shasum": ""
             },
             "require": {
                 "php": ">=7.0.0",
-                "psr/http-message": "^1.0"
+                "psr/http-message": "^1.0 || ^2.0"
             },
             "type": "library",
             "extra": {
@@ -4799,7 +4796,7 @@
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
             "description": "Common interfaces for PSR-7 HTTP message factories",
@@ -4814,9 +4811,9 @@
                 "response"
             ],
             "support": {
-                "source": "https://github.com/php-fig/http-factory/tree/master"
+                "source": "https://github.com/php-fig/http-factory/tree/1.0.2"
             },
-            "time": "2019-04-30T12:38:16+00:00"
+            "time": "2023-04-10T20:10:41+00:00"
         },
         {
             "name": "psr/http-message",
@@ -4873,21 +4870,21 @@
         },
         {
             "name": "psr/http-server-handler",
-            "version": "1.0.1",
+            "version": "1.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/http-server-handler.git",
-                "reference": "aff2f80e33b7f026ec96bb42f63242dc50ffcae7"
+                "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/aff2f80e33b7f026ec96bb42f63242dc50ffcae7",
-                "reference": "aff2f80e33b7f026ec96bb42f63242dc50ffcae7",
+                "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4",
+                "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4",
                 "shasum": ""
             },
             "require": {
                 "php": ">=7.0",
-                "psr/http-message": "^1.0"
+                "psr/http-message": "^1.0 || ^2.0"
             },
             "type": "library",
             "extra": {
@@ -4907,7 +4904,7 @@
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
             "description": "Common interface for HTTP server-side request handler",
@@ -4923,10 +4920,9 @@
                 "server"
             ],
             "support": {
-                "issues": "https://github.com/php-fig/http-server-handler/issues",
-                "source": "https://github.com/php-fig/http-server-handler/tree/master"
+                "source": "https://github.com/php-fig/http-server-handler/tree/1.0.2"
             },
-            "time": "2018-10-30T16:46:14+00:00"
+            "time": "2023-04-10T20:06:20+00:00"
         },
         {
             "name": "ralouphie/getallheaders",
diff --git a/config-templates/module_accounting.php b/config-templates/module_accounting.php
index e1755cd..a1cc2e7 100644
--- a/config-templates/module_accounting.php
+++ b/config-templates/module_accounting.php
@@ -2,10 +2,10 @@
 
 declare(strict_types=1);
 
+use SimpleSAML\Module\accounting\Data\Providers;
+use SimpleSAML\Module\accounting\Data\Stores;
+use SimpleSAML\Module\accounting\Data\Trackers;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Providers;
-use SimpleSAML\Module\accounting\Stores;
-use SimpleSAML\Module\accounting\Trackers;
 
 $config = [
     /**
@@ -56,28 +56,53 @@ $config = [
         //Stores\Jobs\PhpRedis\RedisStore::class,
 
     /**
-     * Default data tracker and provider to be used for accounting and as a source for data display in SSP UI.
-     * This class must implement Trackers\Interfaces\AuthenticationDataTrackerInterface and
-     * Providers\Interfaces\AuthenticationDataProviderInterface
+     * Providers
+     *
+     * VersionedDataProvider classes are used to fetch data about users in order to show it in users profile page UI.
+     * Each provider can also include tracking capability, which will be triggered / used automatically.
+     *
+     * Connected services provider is a class which will be used to provide summary data about services that user
+     * has authenticated at, including authentication count for particular service, and the first and last
+     * authentication time. For OIDC services (Relying Parties, RPs), user can also revoke active tokens.
+     *
+     * Given class must implement interface Providers\Interfaces\ConnectedServicesInterface.
+     *
+     * This option can be set to null, meaning no connected services tracking will take place.
+     */
+    ModuleConfiguration::OPTION_PROVIDER_FOR_CONNECTED_SERVICES =>
+        /**
+         * Default connected services provider which expects Doctrine DBAL compatible connection to be set below.
+         * CurrentDataProvider only gathers current (latest information) about the service and user (there is no
+         * versioning, so it's faster). VersionedDataProvider keeps track of any changes in data about the service
+         * and user.
+         *
+         */
+        Providers\ConnectedServices\DoctrineDbal\CurrentDataProvider::class,
+        //Providers\ConnectedServices\DoctrineDbal\VersionedDataProvider::class,
+
+    /**
+     * Activity provider is a class which will be used to provide list of authentication events which includes info
+     * about the services, user data sent to the service, and the time of authentication.
+     *
+     * Given class must implement interface Providers\Interfaces\ActivityInterfaceData.
+     *
+     * This option can be set to null, meaning no activity tracking will take place.
      */
-    ModuleConfiguration::OPTION_DEFAULT_DATA_TRACKER_AND_PROVIDER =>
+    ModuleConfiguration::OPTION_PROVIDER_FOR_ACTIVITY =>
         /**
-         * Track each authentication event for idp / sp / user combination, and any change in idp / sp metadata or
-         * released user attributes. Each authentication event record will have data used and released at the
-         * time of the authentication event (versioned idp / sp / user data). This tracker can also be
-         * used as an authentication data provider. It expects Doctrine DBAL compatible connection
-         * to be set below. Internally it uses store class
-         * Stores\Data\DoctrineDbal\DoctrineDbal\Versioned\Store::class.
+         * Default activity provider which expects Doctrine DBAL compatible connection to be set below.
+         * Currently only VersionedDataProvider is available, which tracks all changes in services and users.
          */
-        Trackers\Authentication\DoctrineDbal\Versioned\Tracker::class,
+        Providers\Activity\DoctrineDbal\VersionedDataProvider::class,
 
     /**
-     * Additional trackers to run besides default data tracker. These trackers will typically only process and
-     * persist authentication data to proper data store, and won't be used to display data in SSP UI.
-     * These tracker classes must implement Trackers\Interfaces\AuthenticationDataTrackerInterface.
+     * Trackers
+     *
+     * List of additional tracker classes to be used for accounting (processing and persisting authentication data).
+     * These classes must implement Trackers\Interfaces\DataTrackerInterface
      */
     ModuleConfiguration::OPTION_ADDITIONAL_TRACKERS => [
-        // tracker-class
+        // some-tracker-class
     ],
 
     /**
@@ -90,14 +115,26 @@ $config = [
      */
     ModuleConfiguration::OPTION_CLASS_TO_CONNECTION_MAP => [
         /**
-         * Connection key to be used by jobs store class.
+         * Jobs store connection keys.
          */
         Stores\Jobs\DoctrineDbal\Store::class => 'doctrine_dbal_pdo_mysql',
         Stores\Jobs\PhpRedis\RedisStore::class => 'phpredis_class_redis',
         /**
-         * Connection key to be used by this data tracker and provider.
+         * Data provider connection keys.
          */
-        Trackers\Authentication\DoctrineDbal\Versioned\Tracker::class => [
+        Providers\ConnectedServices\DoctrineDbal\VersionedDataProvider::class => [
+            ModuleConfiguration\ConnectionType::MASTER => 'doctrine_dbal_pdo_mysql',
+            ModuleConfiguration\ConnectionType::SLAVE => [
+                'doctrine_dbal_pdo_mysql',
+            ],
+        ],
+        Providers\ConnectedServices\DoctrineDbal\CurrentDataProvider::class => [
+            ModuleConfiguration\ConnectionType::MASTER => 'doctrine_dbal_pdo_mysql',
+            ModuleConfiguration\ConnectionType::SLAVE => [
+                'doctrine_dbal_pdo_mysql',
+            ],
+        ],
+        Providers\Activity\DoctrineDbal\VersionedDataProvider::class => [
             ModuleConfiguration\ConnectionType::MASTER => 'doctrine_dbal_pdo_mysql',
             ModuleConfiguration\ConnectionType::SLAVE => [
                 'doctrine_dbal_pdo_mysql',
@@ -177,7 +214,7 @@ $config = [
     ModuleConfiguration::OPTION_JOB_RUNNER_SHOULD_PAUSE_AFTER_NUMBER_OF_JOBS_PROCESSED => 10,
 
     /**
-     * Tracker data retention policy.
+     * VersionedDataTracker data retention policy.
      *
      * Determines how long the tracked data will be stored. If null, data will be stored indefinitely. Otherwise, it
      * can be set as a duration for DateInterval, examples being below. For this to work, a cron tag must also
@@ -198,8 +235,9 @@ $config = [
     ModuleConfiguration::OPTION_CRON_TAG_FOR_JOB_RUNNER => 'accounting_job_runner',
 
     /**
-     * Tracker data retention policy tag designates the cron tag to use for enforcing data retention policy. Make sure
-     * to add this tag to the cron module configuration if data retention policy is different from null.
+     * VersionedDataTracker data retention policy tag designates the cron tag to use for enforcing data retention
+     * policy. Make sure to add this tag to the cron module configuration if data retention policy is different
+     * from null.
      */
     ModuleConfiguration::OPTION_CRON_TAG_FOR_TRACKER_DATA_RETENTION_POLICY =>
         'accounting_tracker_data_retention_policy',
diff --git a/hooks/hook_cron.php b/hooks/hook_cron.php
index faf0f65..d045e20 100644
--- a/hooks/hook_cron.php
+++ b/hooks/hook_cron.php
@@ -4,11 +4,11 @@ declare(strict_types=1);
 
 use Psr\Log\LoggerInterface;
 use SimpleSAML\Configuration;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Module\accounting\Services\JobRunner;
-use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\Logger;
-use SimpleSAML\Module\accounting\Trackers\Builders\AuthenticationDataTrackerBuilder;
+use SimpleSAML\Module\accounting\Services\TrackerResolver;
 
 /**
  * @param array $cronInfo
@@ -51,7 +51,7 @@ function accounting_hook_cron(array &$cronInfo): void
     }
 
     /**
-     * Tracker data retention policy handling.
+     * VersionedDataTracker data retention policy handling.
      */
     $cronTagForTrackerDataRetentionPolicy = $moduleConfiguration->getCronTagForTrackerDataRetentionPolicy();
     try {
@@ -82,16 +82,11 @@ function handleDataRetentionPolicy(
     HelpersManager $helpersManager,
     DateInterval $retentionPolicy
 ): void {
-    // Handle default data tracker and provider
-    (new AuthenticationDataTrackerBuilder($moduleConfiguration, $logger, $helpersManager))
-        ->build($moduleConfiguration->getDefaultDataTrackerAndProviderClass())
-        ->enforceDataRetentionPolicy($retentionPolicy);
 
-    $additionalTrackers = $moduleConfiguration->getAdditionalTrackers();
+    $trackers = (new TrackerResolver($moduleConfiguration, $logger, $helpersManager))->fromModuleConfiguration();
 
-    foreach ($additionalTrackers as $tracker) {
-        (new AuthenticationDataTrackerBuilder($moduleConfiguration, $logger, $helpersManager))
-            ->build($tracker)
-            ->enforceDataRetentionPolicy($retentionPolicy);
+    foreach ($trackers as $trackerClass => $tracker) {
+        $logger->info('Applying data retention policy for class ' . $trackerClass);
+        $tracker->enforceDataRetentionPolicy($retentionPolicy);
     }
 }
\ No newline at end of file
diff --git a/routing/routes/routes.yml b/routing/routes/routes.yml
index 1d2d429..dcd41bc 100644
--- a/routing/routes/routes.yml
+++ b/routing/routes/routes.yml
@@ -15,15 +15,6 @@ accounting-user-activity:
     path: /user/activity
     controller: SimpleSAML\Module\accounting\Http\Controllers\User\Profile::activity
 
-accounting-user-oidc-tokens:
-    path: /user/oidc-tokens
-    controller: SimpleSAML\Module\accounting\Http\Controllers\User\Profile::oidcTokens
-
-accounting-user-oidc-token-revoke:
-    path: /user/oidc-tokens/revoke
-    controller: SimpleSAML\Module\accounting\Http\Controllers\User\Profile::oidcTokenRevoke
-    methods: POST
-
 accounting-user-oidc-token-revoke-xhr:
     path: /user/oidc-tokens/revoke-xhr
     controller: SimpleSAML\Module\accounting\Http\Controllers\User\Profile::oidcTokenRevokeXhr
diff --git a/src/Auth/Process/Accounting.php b/src/Auth/Process/Accounting.php
index ecb6102..c731907 100644
--- a/src/Auth/Process/Accounting.php
+++ b/src/Auth/Process/Accounting.php
@@ -6,13 +6,13 @@ namespace SimpleSAML\Module\accounting\Auth\Process;
 
 use Psr\Log\LoggerInterface;
 use SimpleSAML\Auth\ProcessingFilter;
+use SimpleSAML\Module\accounting\Data\Stores\Builders\JobsStoreBuilder;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Module\accounting\Services\Logger;
-use SimpleSAML\Module\accounting\Stores\Builders\JobsStoreBuilder;
-use SimpleSAML\Module\accounting\Trackers\Builders\AuthenticationDataTrackerBuilder;
+use SimpleSAML\Module\accounting\Services\TrackerResolver;
 use Throwable;
 
 class Accounting extends ProcessingFilter
@@ -20,8 +20,8 @@ class Accounting extends ProcessingFilter
     protected ModuleConfiguration $moduleConfiguration;
     protected JobsStoreBuilder $jobsStoreBuilder;
     protected LoggerInterface $logger;
-    protected AuthenticationDataTrackerBuilder $authenticationDataTrackerBuilder;
     protected HelpersManager $helpersManager;
+    protected TrackerResolver $trackerResolver;
 
     /**
      * @param array $config
@@ -30,7 +30,7 @@ class Accounting extends ProcessingFilter
      * @param LoggerInterface|null $logger
      * @param HelpersManager|null $helpersManager
      * @param JobsStoreBuilder|null $jobsStoreBuilder
-     * @param AuthenticationDataTrackerBuilder|null $authenticationDataTrackerBuilder
+     * @param TrackerResolver|null $trackerResolver
      */
     public function __construct(
         array &$config,
@@ -39,7 +39,7 @@ class Accounting extends ProcessingFilter
         LoggerInterface $logger = null,
         HelpersManager $helpersManager = null,
         JobsStoreBuilder $jobsStoreBuilder = null,
-        AuthenticationDataTrackerBuilder $authenticationDataTrackerBuilder = null
+        TrackerResolver $trackerResolver = null
     ) {
         parent::__construct($config, $reserved);
 
@@ -49,8 +49,11 @@ class Accounting extends ProcessingFilter
         $this->jobsStoreBuilder = $jobsStoreBuilder ??
             new JobsStoreBuilder($this->moduleConfiguration, $this->logger, $this->helpersManager);
 
-        $this->authenticationDataTrackerBuilder = $authenticationDataTrackerBuilder ??
-            new AuthenticationDataTrackerBuilder($this->moduleConfiguration, $this->logger, $this->helpersManager);
+        $this->trackerResolver = $trackerResolver ?? new TrackerResolver(
+            $this->moduleConfiguration,
+            $this->logger,
+            $this->helpersManager
+        );
     }
 
     /**
@@ -58,26 +61,28 @@ class Accounting extends ProcessingFilter
      */
     public function process(array &$state): void
     {
-        try {
-            $resolvedState = $this->helpersManager->getAuthenticationEventStateResolver()->fromStateArray($state);
+        $this->logger->debug('Accounting started.', $state);
 
-            $authenticationEvent = new Event($resolvedState);
+        try {
+            $authenticationEvent = new Event(
+                $this->helpersManager->getAuthenticationEventStateResolver()->fromStateArray($state)
+            );
 
             if ($this->isAccountingProcessingTypeAsynchronous()) {
                 // Only create authentication event job for later processing...
+                $this->logger->debug('Accounting type is asynchronous, creating job for later processing.');
                 $this->createAuthenticationEventJob($authenticationEvent);
                 return;
             }
 
-            // Accounting type is synchronous, so do the processing right away...
-            $configuredTrackers = array_merge(
-                [$this->moduleConfiguration->getDefaultDataTrackerAndProviderClass()],
-                $this->moduleConfiguration->getAdditionalTrackers()
-            );
+            $this->logger->debug('Accounting type is synchronous, processing now.');
 
-            foreach ($configuredTrackers as $tracker) {
-                ($this->authenticationDataTrackerBuilder->build($tracker))->process($authenticationEvent);
+            foreach ($this->trackerResolver->fromModuleConfiguration() as $trackerClass => $tracker) {
+                    $this->logger->debug(sprintf('Processing tracker for class %s.', $trackerClass));
+                    $tracker->process($authenticationEvent);
             }
+
+            $this->logger->debug('Finished with tracker processing.');
         } catch (Throwable $exception) {
             $message = sprintf('Accounting error, skipping... Error was: %s.', $exception->getMessage());
             $this->logger->error($message, $state);
diff --git a/src/Data/Providers/Activity/DoctrineDbal/VersionedDataProvider.php b/src/Data/Providers/Activity/DoctrineDbal/VersionedDataProvider.php
new file mode 100644
index 0000000..e0b6f63
--- /dev/null
+++ b/src/Data/Providers/Activity/DoctrineDbal/VersionedDataProvider.php
@@ -0,0 +1,95 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Providers\Activity\DoctrineDbal;
+
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Providers\Interfaces\ActivityInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store;
+use SimpleSAML\Module\accounting\Data\Trackers\Activity\DoctrineDbal\VersionedDataTracker;
+use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface;
+use SimpleSAML\Module\accounting\Entities\Activity;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+
+class VersionedDataProvider implements ActivityInterface
+{
+    protected ModuleConfiguration $moduleConfiguration;
+    protected LoggerInterface $logger;
+    protected Store $store;
+
+    /**
+     * @throws StoreException
+     */
+    public function __construct(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionType = ModuleConfiguration\ConnectionType::SLAVE,
+        Store $store = null
+    ) {
+        $this->moduleConfiguration = $moduleConfiguration;
+        $this->logger = $logger;
+
+        $this->store = $store ?? new Store(
+            $this->moduleConfiguration,
+            $this->logger,
+            $this->moduleConfiguration->getClassConnectionKey(self::class),
+            $connectionType
+        );
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public static function build(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionType = ModuleConfiguration\ConnectionType::SLAVE
+    ): self {
+        return new self($moduleConfiguration, $logger, $connectionType);
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function needsSetup(): bool
+    {
+        return $this->store->needsSetup();
+    }
+
+    /**
+     * @throws StoreException
+     * @throws MigrationException
+     */
+    public function runSetup(): void
+    {
+        if (! $this->needsSetup()) {
+            $this->logger->warning('Run setup called, however setup is not needed.');
+            return;
+        }
+
+        $this->store->runSetup();
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function getActivity(string $userIdentifier, int $maxResults, int $firstResult): Activity\Bag
+    {
+        return $this->store->getActivity($userIdentifier, $maxResults, $firstResult);
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function getTracker(): ?DataTrackerInterface
+    {
+        return new VersionedDataTracker(
+            $this->moduleConfiguration,
+            $this->logger,
+            ModuleConfiguration\ConnectionType::MASTER,
+        );
+    }
+}
diff --git a/src/Data/Providers/Builders/DataProviderBuilder.php b/src/Data/Providers/Builders/DataProviderBuilder.php
new file mode 100644
index 0000000..7bba072
--- /dev/null
+++ b/src/Data/Providers/Builders/DataProviderBuilder.php
@@ -0,0 +1,105 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Providers\Builders;
+
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Providers\Interfaces\ActivityInterface;
+use SimpleSAML\Module\accounting\Data\Providers\Interfaces\ConnectedServicesInterface;
+use SimpleSAML\Module\accounting\Data\Providers\Interfaces\DataProviderInterface;
+use SimpleSAML\Module\accounting\Exceptions\Exception;
+use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
+use Throwable;
+
+class DataProviderBuilder
+{
+    protected ModuleConfiguration $moduleConfiguration;
+    protected LoggerInterface $logger;
+    protected HelpersManager $helpersManager;
+
+    public function __construct(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        HelpersManager $helpersManager
+    ) {
+        $this->moduleConfiguration = $moduleConfiguration;
+        $this->logger = $logger;
+        $this->helpersManager = $helpersManager;
+    }
+
+    /**
+     * @throws Exception
+     */
+    public function build(
+        string $class,
+        string $connectionType = ModuleConfiguration\ConnectionType::SLAVE
+    ): DataProviderInterface {
+        try {
+            // Make sure that the class implements proper interface
+            if (!is_subclass_of($class, DataProviderInterface::class)) {
+                $message = sprintf(
+                    'Class %s does not implement interface %s.',
+                    $class,
+                    DataProviderInterface::class
+                );
+                throw new UnexpectedValueException($message);
+            }
+
+            // Build...
+            /** @var DataProviderInterface $provider */
+            $provider = $this->helpersManager->getInstanceBuilderUsingModuleConfiguration()->build(
+                $class,
+                $this->moduleConfiguration,
+                $this->logger,
+                [$connectionType]
+            );
+        } catch (Throwable $exception) {
+            $message = sprintf('Error building instance for class %s. Error was: %s', $class, $exception->getMessage());
+            throw new Exception($message, (int)$exception->getCode(), $exception);
+        }
+
+        return $provider;
+    }
+
+    public function buildActivityProvider(
+        string $class,
+        string $connectionType = ModuleConfiguration\ConnectionType::SLAVE
+    ): ActivityInterface {
+        $activityProvider =  $this->build($class, $connectionType);
+
+        if (!is_subclass_of($activityProvider, ActivityInterface::class)) {
+            $message = sprintf(
+                'Class %s does not implement interface %s.',
+                $class,
+                ActivityInterface::class
+            );
+            throw new UnexpectedValueException($message);
+        }
+
+        return $activityProvider;
+    }
+
+    /**
+     * @throws Exception
+     */
+    public function buildConnectedServicesProvider(
+        string $class,
+        string $connectionType = ModuleConfiguration\ConnectionType::SLAVE
+    ): ConnectedServicesInterface {
+        $connectedServicesProvider =  $this->build($class, $connectionType);
+
+        if (!is_subclass_of($connectedServicesProvider, ConnectedServicesInterface::class)) {
+            $message = sprintf(
+                'Class %s does not implement interface %s.',
+                $class,
+                ActivityInterface::class
+            );
+            throw new UnexpectedValueException($message);
+        }
+
+        return $connectedServicesProvider;
+    }
+}
diff --git a/src/Data/Providers/ConnectedServices/DoctrineDbal/CurrentDataProvider.php b/src/Data/Providers/ConnectedServices/DoctrineDbal/CurrentDataProvider.php
new file mode 100644
index 0000000..323c867
--- /dev/null
+++ b/src/Data/Providers/ConnectedServices/DoctrineDbal/CurrentDataProvider.php
@@ -0,0 +1,95 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Providers\ConnectedServices\DoctrineDbal;
+
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Providers\Interfaces\ConnectedServicesInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store;
+use SimpleSAML\Module\accounting\Data\Trackers\ConnectedServices\DoctrineDbal\CurrentDataTracker;
+use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface;
+use SimpleSAML\Module\accounting\Entities\ConnectedService;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+
+class CurrentDataProvider implements ConnectedServicesInterface
+{
+    protected ModuleConfiguration $moduleConfiguration;
+    protected LoggerInterface $logger;
+    protected Store $store;
+
+    /**
+     * @throws StoreException
+     */
+    public function __construct(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionType = ModuleConfiguration\ConnectionType::SLAVE,
+        Store $store = null
+    ) {
+        $this->moduleConfiguration = $moduleConfiguration;
+        $this->logger = $logger;
+
+        $this->store = $store ?? new Store(
+            $this->moduleConfiguration,
+            $this->logger,
+            $this->moduleConfiguration->getClassConnectionKey(self::class),
+            $connectionType
+        );
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public static function build(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionType = ModuleConfiguration\ConnectionType::SLAVE
+    ): self {
+        return new self($moduleConfiguration, $logger, $connectionType);
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function needsSetup(): bool
+    {
+        return $this->store->needsSetup();
+    }
+
+    /**
+     * @throws StoreException
+     * @throws MigrationException
+     */
+    public function runSetup(): void
+    {
+        if (! $this->needsSetup()) {
+            $this->logger->warning('Run setup called, however setup is not needed.');
+            return;
+        }
+
+        $this->store->runSetup();
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function getConnectedServices(string $userIdentifier): ConnectedService\Bag
+    {
+        return $this->store->getConnectedServices($userIdentifier);
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function getTracker(): ?DataTrackerInterface
+    {
+        return new CurrentDataTracker(
+            $this->moduleConfiguration,
+            $this->logger,
+            ModuleConfiguration\ConnectionType::MASTER,
+        );
+    }
+}
diff --git a/src/Data/Providers/ConnectedServices/DoctrineDbal/VersionedDataProvider.php b/src/Data/Providers/ConnectedServices/DoctrineDbal/VersionedDataProvider.php
new file mode 100644
index 0000000..788da27
--- /dev/null
+++ b/src/Data/Providers/ConnectedServices/DoctrineDbal/VersionedDataProvider.php
@@ -0,0 +1,95 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Providers\ConnectedServices\DoctrineDbal;
+
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Providers\Interfaces\ConnectedServicesInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store;
+use SimpleSAML\Module\accounting\Data\Trackers\ConnectedServices\DoctrineDbal\VersionedDataTracker;
+use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface;
+use SimpleSAML\Module\accounting\Entities\ConnectedService;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+
+class VersionedDataProvider implements ConnectedServicesInterface
+{
+    protected ModuleConfiguration $moduleConfiguration;
+    protected LoggerInterface $logger;
+    protected Store $store;
+
+    /**
+     * @throws StoreException
+     */
+    public function __construct(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionType = ModuleConfiguration\ConnectionType::SLAVE,
+        Store $store = null
+    ) {
+        $this->moduleConfiguration = $moduleConfiguration;
+        $this->logger = $logger;
+
+        $this->store = $store ?? new Store(
+            $this->moduleConfiguration,
+            $this->logger,
+            $this->moduleConfiguration->getClassConnectionKey(self::class),
+            $connectionType
+        );
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public static function build(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionType = ModuleConfiguration\ConnectionType::SLAVE
+    ): self {
+        return new self($moduleConfiguration, $logger, $connectionType);
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function needsSetup(): bool
+    {
+        return $this->store->needsSetup();
+    }
+
+    /**
+     * @throws StoreException
+     * @throws MigrationException
+     */
+    public function runSetup(): void
+    {
+        if (! $this->needsSetup()) {
+            $this->logger->warning('Run setup called, however setup is not needed.');
+            return;
+        }
+
+        $this->store->runSetup();
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function getConnectedServices(string $userIdentifier): ConnectedService\Bag
+    {
+        return $this->store->getConnectedServices($userIdentifier);
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function getTracker(): ?DataTrackerInterface
+    {
+        return new VersionedDataTracker(
+            $this->moduleConfiguration,
+            $this->logger,
+            ModuleConfiguration\ConnectionType::MASTER,
+        );
+    }
+}
diff --git a/src/Providers/Interfaces/AuthenticationDataProviderInterface.php b/src/Data/Providers/Interfaces/ActivityInterface.php
similarity index 63%
rename from src/Providers/Interfaces/AuthenticationDataProviderInterface.php
rename to src/Data/Providers/Interfaces/ActivityInterface.php
index 8c60297..0102c89 100644
--- a/src/Providers/Interfaces/AuthenticationDataProviderInterface.php
+++ b/src/Data/Providers/Interfaces/ActivityInterface.php
@@ -2,15 +2,15 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Providers\Interfaces;
+namespace SimpleSAML\Module\accounting\Data\Providers\Interfaces;
 
 use Psr\Log\LoggerInterface;
 use SimpleSAML\Module\accounting\Entities\Activity;
-use SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider;
 use SimpleSAML\Module\accounting\Interfaces\BuildableUsingModuleConfigurationInterface;
+use SimpleSAML\Module\accounting\Interfaces\SetupableInterface;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 
-interface AuthenticationDataProviderInterface extends BuildableUsingModuleConfigurationInterface
+interface ActivityInterface extends DataProviderInterface
 {
     public static function build(
         ModuleConfiguration $moduleConfiguration,
@@ -18,7 +18,5 @@ interface AuthenticationDataProviderInterface extends BuildableUsingModuleConfig
         string $connectionType = ModuleConfiguration\ConnectionType::MASTER
     ): self;
 
-    public function getConnectedServiceProviders(string $userIdentifier): ConnectedServiceProvider\Bag;
-
     public function getActivity(string $userIdentifier, int $maxResults, int $firstResult): Activity\Bag;
 }
diff --git a/src/Data/Providers/Interfaces/ConnectedServicesInterface.php b/src/Data/Providers/Interfaces/ConnectedServicesInterface.php
new file mode 100644
index 0000000..fcd56af
--- /dev/null
+++ b/src/Data/Providers/Interfaces/ConnectedServicesInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Providers\Interfaces;
+
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Entities\ConnectedService;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+
+interface ConnectedServicesInterface extends DataProviderInterface
+{
+    public static function build(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionType = ModuleConfiguration\ConnectionType::SLAVE
+    ): self;
+
+    public function getConnectedServices(string $userIdentifier): ConnectedService\Bag;
+}
diff --git a/src/Data/Providers/Interfaces/DataProviderInterface.php b/src/Data/Providers/Interfaces/DataProviderInterface.php
new file mode 100644
index 0000000..cc42946
--- /dev/null
+++ b/src/Data/Providers/Interfaces/DataProviderInterface.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Providers\Interfaces;
+
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface;
+use SimpleSAML\Module\accounting\Interfaces\BuildableUsingModuleConfigurationInterface;
+use SimpleSAML\Module\accounting\Interfaces\SetupableInterface;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+
+interface DataProviderInterface extends BuildableUsingModuleConfigurationInterface, SetupableInterface
+{
+    public static function build(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionType = ModuleConfiguration\ConnectionType::SLAVE
+    ): self;
+
+    public function getTracker(): ?DataTrackerInterface;
+}
diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store.php
new file mode 100644
index 0000000..149b8bc
--- /dev/null
+++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store.php
@@ -0,0 +1,146 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned;
+
+use DateTimeImmutable;
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\RawActivity;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Repository;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store as BaseStore;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory;
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\ActivityInterface;
+use SimpleSAML\Module\accounting\Entities\Activity;
+use SimpleSAML\Module\accounting\Entities\Authentication\Event;
+use SimpleSAML\Module\accounting\Entities\User;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
+use Throwable;
+
+class Store extends BaseStore implements ActivityInterface
+{
+    protected Repository $repository;
+
+    /**
+     * @throws StoreException
+     */
+    public function __construct(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionKey = null,
+        string $connectionType = ModuleConfiguration\ConnectionType::MASTER,
+        Factory $connectionFactory = null,
+        HelpersManager $helpersManager = null,
+        Repository $repository = null
+    ) {
+        parent::__construct(
+            $moduleConfiguration,
+            $logger,
+            $connectionKey,
+            $connectionType,
+            $connectionFactory,
+            $helpersManager,
+            $repository
+        );
+
+        $this->repository = $repository ?? new Repository($this->connection, $this->logger);
+    }
+
+    /**
+     * Build store instance.
+     * @throws StoreException
+     */
+    public static function build(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionKey = null,
+        string $connectionType = ModuleConfiguration\ConnectionType::MASTER
+    ): self {
+        return new self(
+            $moduleConfiguration,
+            $logger,
+            $connectionKey,
+            $connectionType
+        );
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function persist(Event $authenticationEvent): void
+    {
+        $hashDecoratedState = new HashDecoratedState($authenticationEvent->getState());
+
+        $idpId = $this->resolveIdpId($hashDecoratedState);
+        $idpVersionId = $this->resolveIdpVersionId($idpId, $hashDecoratedState);
+        $spId = $this->resolveSpId($hashDecoratedState);
+        $spVersionId = $this->resolveSpVersionId($spId, $hashDecoratedState);
+        $userId = $this->resolveUserId($hashDecoratedState);
+        $userVersionId = $this->resolveUserVersionId($userId, $hashDecoratedState);
+        $idpSpUserVersionId = $this->resolveIdpSpUserVersionId($idpVersionId, $spVersionId, $userVersionId);
+
+        $this->repository->insertAuthenticationEvent(
+            $idpSpUserVersionId,
+            $authenticationEvent->getHappenedAt(),
+            $authenticationEvent->getState()->getClientIpAddress(),
+            $authenticationEvent->getState()->getAuthenticationProtocol()->getDesignation()
+        );
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function getActivity(string $userIdentifier, int $maxResults, int $firstResult): Activity\Bag
+    {
+        $userIdentifierHashSha256 = $this->helpersManager->getHash()->getSha256($userIdentifier);
+
+        $results =  $this->repository->getActivity($userIdentifierHashSha256, $maxResults, $firstResult);
+
+        $activityBag = new Activity\Bag();
+
+        if (empty($results)) {
+            return $activityBag;
+        }
+
+        try {
+            /** @var array $result */
+            foreach ($results as $result) {
+                $rawActivity = new RawActivity($result, $this->connection->dbal()->getDatabasePlatform());
+                $serviceProvider = $this->helpersManager
+                    ->getProviderResolver()
+                    ->forServiceFromMetadataArray($rawActivity->getServiceProviderMetadata());
+                $user = new User($rawActivity->getUserAttributes());
+
+                $activityBag->add(
+                    new Activity(
+                        $serviceProvider,
+                        $user,
+                        $rawActivity->getHappenedAt(),
+                        $rawActivity->getClientIpAddress(),
+                        $rawActivity->getAuthenticationProtocolDesignation()
+                    )
+                );
+            }
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error populating activity bag. Error was: %s',
+                $exception->getMessage()
+            );
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+
+        return $activityBag;
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function deleteDataOlderThan(DateTimeImmutable $dateTime): void
+    {
+        // Only delete authentication events. VersionedDataProvider data (IdP / SP metadata, user attributes) remain.
+        $this->repository->deleteAuthenticationEventsOlderThan($dateTime);
+    }
+}
diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTable.php
new file mode 100644
index 0000000..ad3f84c
--- /dev/null
+++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTable.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpTable;
+
+class Version20220801000000CreateIdpTable extends CreateIdpTable
+{
+}
diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTable.php
new file mode 100644
index 0000000..31bb2d4
--- /dev/null
+++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTable.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
+
+class Version20220801000100CreateIdpVersionTable extends Migrations\CreateIdpVersionTable
+{
+}
diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTable.php
new file mode 100644
index 0000000..a340560
--- /dev/null
+++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTable.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateSpTable;
+
+class Version20220801000200CreateSpTable extends CreateSpTable
+{
+}
diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTable.php
new file mode 100644
index 0000000..846629b
--- /dev/null
+++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTable.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
+
+class Version20220801000300CreateSpVersionTable extends Migrations\CreateSpVersionTable
+{
+}
diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTable.php
new file mode 100644
index 0000000..de24d65
--- /dev/null
+++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTable.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable;
+
+class Version20220801000400CreateUserTable extends CreateUserTable
+{
+}
diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTable.php
new file mode 100644
index 0000000..bd429ee
--- /dev/null
+++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTable.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
+
+class Version20220801000500CreateUserVersionTable extends Migrations\CreateUserVersionTable
+{
+}
diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTable.php
new file mode 100644
index 0000000..4ac4a69
--- /dev/null
+++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTable.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
+
+class Version20220801000600CreateIdpSpUserVersionTable extends Migrations\CreateIdpSpUserVersionTable
+{
+}
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php
similarity index 88%
rename from src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php
rename to src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php
index 65940b5..d477b56 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php
+++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTable.php
@@ -2,13 +2,13 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations;
 
 use Doctrine\DBAL\Schema\Table;
 use Doctrine\DBAL\Types\Types;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\TableConstants;
 use Throwable;
 
 use function sprintf;
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawActivity.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RawActivity.php
similarity index 96%
rename from src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawActivity.php
rename to src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RawActivity.php
index b943311..ed7b42c 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawActivity.php
+++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RawActivity.php
@@ -2,12 +2,12 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store;
 
 use DateTimeImmutable;
 use Doctrine\DBAL\Platforms\AbstractPlatform;
+use SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity;
 use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
-use SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractRawEntity;
 
 class RawActivity extends AbstractRawEntity
 {
diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Repository.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Repository.php
new file mode 100644
index 0000000..7071f00
--- /dev/null
+++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Repository.php
@@ -0,0 +1,243 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store;
+
+use DateTimeImmutable;
+use Doctrine\DBAL\Types\Types;
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository as BaseRepository;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants
+    as BaseTableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use Throwable;
+
+class Repository extends BaseRepository
+{
+    protected string $tableNameAuthenticationEvent;
+
+    public function __construct(Connection $connection, LoggerInterface $logger)
+    {
+        parent::__construct($connection, $logger);
+
+        $this->tableNameAuthenticationEvent =
+            $this->preparePrefixedTableName(
+                TableConstants::TABLE_NAME_AUTHENTICATION_EVENT
+            );
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function insertAuthenticationEvent(
+        int $idpSpUserVersionId,
+        DateTimeImmutable $happenedAt,
+        string $clientIpAddress = null,
+        string $authenticationProtocolDesignation = null,
+        DateTimeImmutable $createdAt = null
+    ): void {
+        try {
+            $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+            $createdAt = $createdAt ?? new DateTimeImmutable();
+
+            $queryBuilder->insert($this->tableNameAuthenticationEvent)
+                ->values(
+                    [
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_IDP_SP_USER_VERSION_ID => ':' .
+                            TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_IDP_SP_USER_VERSION_ID,
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT => ':' .
+                            TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT,
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS => ':' .
+                            TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS,
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION =>
+                            ':' .
+                            TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION,
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT => ':' .
+                            TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT,
+                    ]
+                )
+                ->setParameters(
+                    [
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_IDP_SP_USER_VERSION_ID =>
+                            $idpSpUserVersionId,
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT => $happenedAt,
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS => $clientIpAddress,
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION =>
+                            $authenticationProtocolDesignation,
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT => $createdAt,
+                    ],
+                    [
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_IDP_SP_USER_VERSION_ID =>
+                            Types::BIGINT,
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT =>
+                            Types::DATETIMETZ_IMMUTABLE,
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS =>
+                            Types::STRING,
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION =>
+                            Types::STRING,
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT =>
+                            Types::DATETIMETZ_IMMUTABLE,
+                    ]
+                );
+
+            $queryBuilder->executeStatement();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing query to insert AuthenticationEvent. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->error($message, compact('idpSpUserVersionId'));
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function getActivity(string $userIdentifierHashSha256, int $maxResults, int $firstResult): array
+    {
+        try {
+            $authenticationEventsQueryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+            /** @psalm-suppress TooManyArguments */
+            $authenticationEventsQueryBuilder->select(
+                //'vae.happened_at',
+                TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' .
+                TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT,
+                TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' .
+                TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS,
+                TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' .
+                TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION,
+                //'vsv.metadata AS sp_metadata',
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' .
+                BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_METADATA .
+                ' AS ' . TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA,
+                //'vuv.attributes AS user_attributes'
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES . ' AS ' .
+                TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES
+            )->from(
+                //'vds_authentication_event', 'vae'
+                $this->tableNameAuthenticationEvent,
+                //'vae'
+                TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT
+            )
+                ->leftJoin(
+                    //'vae',
+                    TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT,
+                    //'vds_idp_sp_user_version',
+                    $this->tableNameIdpSpUserVersion,
+                    //'visuv',
+                    BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
+                    //'vae.idp_sp_user_version_id = visuv.id'
+                    TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' .
+                    TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_IDP_SP_USER_VERSION_ID . ' = ' .
+                    BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
+                    BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID
+                )
+                ->leftJoin(
+                    //'visuv',
+                    BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
+                    //'vds_sp_version',
+                    $this->tableNameSpVersion,
+                    //'vsv',
+                    BaseTableConstants::TABLE_ALIAS_SP_VERSION,
+                    //'visuv.sp_version_id = vsv.id'
+                    BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
+                    BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID . ' = ' .
+                    BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' .
+                    BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID
+                )
+                ->leftJoin(
+                    //'vsv',
+                    BaseTableConstants::TABLE_ALIAS_SP_VERSION,
+                    //'vds_sp',
+                    $this->tableNameSp,
+                    //'vs',
+                    BaseTableConstants::TABLE_ALIAS_SP,
+                    //'vsv.sp_id = vs.id'
+                    BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' .
+                    BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_SP_ID .
+                    ' = ' . BaseTableConstants::TABLE_ALIAS_SP . '.' .
+                    BaseTableConstants::TABLE_SP_COLUMN_NAME_ID
+                )
+                ->leftJoin(
+                    //'visuv',
+                    BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
+                    //'vds_user_version',
+                    $this->tableNameUserVersion,
+                    //'vuv',
+                    BaseTableConstants::TABLE_ALIAS_USER_VERSION,
+                    //'visuv.user_version_id = vuv.id'
+                    BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
+                    BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID . ' = ' .
+                    BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' .
+                    BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID
+                )
+                ->leftJoin(
+                    //'vuv',
+                    BaseTableConstants::TABLE_ALIAS_USER_VERSION,
+                    //'vds_user',
+                    $this->tableNameUser,
+                    //'vu',
+                    BaseTableConstants::TABLE_ALIAS_USER,
+                    //'vuv.user_id = vu.id'
+                    BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' .
+                    BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID . ' = ' .
+                    BaseTableConstants::TABLE_ALIAS_USER . '.' .
+                    BaseTableConstants::TABLE_USER_COLUMN_NAME_ID
+                )
+                ->where(
+                    //'vu.identifier_hash_sha256 = ' .
+                    BaseTableConstants::TABLE_ALIAS_USER . '.' .
+                    BaseTableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 . ' = ' .
+                    $authenticationEventsQueryBuilder->createNamedParameter($userIdentifierHashSha256)
+                )
+                ->orderBy(
+                //'vae.id',
+                    TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' .
+                    TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_ID,
+                    'DESC'
+                )
+            ->setMaxResults($maxResults)
+            ->setFirstResult($firstResult);
+
+            return $authenticationEventsQueryBuilder->executeQuery()->fetchAllAssociative();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing query to get connected organizations. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->error($message, compact('userIdentifierHashSha256'));
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function deleteAuthenticationEventsOlderThan(DateTimeImmutable $dateTime): void
+    {
+        try {
+            $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+            $queryBuilder->delete($this->tableNameAuthenticationEvent)
+                ->where(
+                    $queryBuilder->expr()->lt(
+                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT,
+                        $queryBuilder->createNamedParameter($dateTime, Types::DATETIME_IMMUTABLE)
+                    )
+                )->executeStatement();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing query to delete old authentication events. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->error($message);
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+}
diff --git a/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/TableConstants.php b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/TableConstants.php
new file mode 100644
index 0000000..df5b36c
--- /dev/null
+++ b/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/TableConstants.php
@@ -0,0 +1,30 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants
+    as BaseTableConstants;
+
+class TableConstants
+{
+    // Table 'authentication_event'.
+    public const TABLE_NAME_AUTHENTICATION_EVENT = 'authentication_event';
+    public const TABLE_ALIAS_AUTHENTICATION_EVENT = BaseTableConstants::TABLE_PREFIX . 'ae';
+    public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_ID = 'id';
+    public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_IDP_SP_USER_VERSION_ID = 'idp_sp_user_version_id';
+    public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT = 'happened_at';
+    public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS = 'client_ip_address';
+    public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION =
+        'authentication_protocol_designation';
+    public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT = 'created_at';
+
+    // Entity 'Activity' related.
+    public const ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA = 'sp_metadata';
+    public const ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES = 'user_attributes';
+    public const ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT = 'happened_at';
+    public const ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS = 'client_ip_address';
+    public const ENTITY_ACTIVITY_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION =
+        'authentication_protocol_designation';
+}
diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store.php
new file mode 100644
index 0000000..0090783
--- /dev/null
+++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store.php
@@ -0,0 +1,242 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current;
+
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Repository;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState;
+use SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory;
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\StoreInterface;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
+use Throwable;
+
+class Store extends AbstractStore implements StoreInterface
+{
+    protected HelpersManager $helpersManager;
+    private Repository $repository;
+
+    /**
+     * @throws StoreException
+     */
+    public function __construct(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionKey = null,
+        string $connectionType = ModuleConfiguration\ConnectionType::MASTER,
+        Factory $connectionFactory = null,
+        HelpersManager $helpersManager = null,
+        Repository $repository = null
+    ) {
+        parent::__construct($moduleConfiguration, $logger, $connectionKey, $connectionType, $connectionFactory);
+
+        $this->helpersManager = $helpersManager ?? new HelpersManager();
+        $this->repository = $repository ?? new Repository($this->connection, $this->logger);
+    }
+
+    /**
+     * Build store instance.
+     * @throws StoreException
+     */
+    public static function build(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionKey = null,
+        string $connectionType = ModuleConfiguration\ConnectionType::MASTER
+    ): self {
+        return new self(
+            $moduleConfiguration,
+            $logger,
+            $connectionKey,
+            $connectionType
+        );
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function resolveSpId(HashDecoratedState $hashDecoratedState): int
+    {
+        $spEntityIdHashSha256 = $hashDecoratedState->getServiceProviderEntityIdHashSha256();
+
+        // Check if it already exists.
+        try {
+            $result = $this->repository->getSp($spEntityIdHashSha256);
+            $sp = $result->fetchAssociative();
+
+            if ($sp !== false) {
+                $spId = (int)$sp[TableConstants::TABLE_SP_COLUMN_NAME_ID];
+                // If metadata hash is different, update metadata.
+                if (
+                    $sp[TableConstants::TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256] !==
+                    $hashDecoratedState->getServiceProviderMetadataArrayHashSha256()
+                ) {
+                    $this->repository->updateSp(
+                        $spId,
+                        serialize($hashDecoratedState->getState()->getServiceProviderMetadata()),
+                        $hashDecoratedState->getServiceProviderMetadataArrayHashSha256()
+                    );
+                }
+                return $spId;
+            }
+        } catch (Throwable $exception) {
+            $message = sprintf('Error resolving SP ID. Error was: %s.', $exception->getMessage());
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+
+        // Create new
+        try {
+            $this->repository->insertSp(
+                $hashDecoratedState->getState()->getServiceProviderEntityId(),
+                $spEntityIdHashSha256,
+                serialize($hashDecoratedState->getState()->getServiceProviderMetadata()),
+                $hashDecoratedState->getServiceProviderMetadataArrayHashSha256()
+            );
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error inserting new SP, however, continuing in case of race condition. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->warning($message);
+        }
+
+        // Try again, this time it should exist...
+        try {
+            $result = $this->repository->getSp($spEntityIdHashSha256);
+            $spIdNew = $result->fetchOne();
+
+            if ($spIdNew !== false) {
+                return (int)$spIdNew;
+            }
+
+            $message = sprintf(
+                'Error fetching SP ID even after insertion for entity ID hash SHA256 %s.',
+                $spEntityIdHashSha256
+            );
+            throw new StoreException($message);
+        } catch (Throwable $exception) {
+            $message = sprintf('Error resolving SP ID. Error was: %s.', $exception->getMessage());
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function resolveUserId(HashDecoratedState $hashDecoratedState): int
+    {
+        $userIdentifierAttributeName = $this->moduleConfiguration->getUserIdAttributeName();
+
+        $userIdentifierValue = $hashDecoratedState->getState()->getFirstAttributeValue($userIdentifierAttributeName);
+        if ($userIdentifierValue === null) {
+            $message = sprintf('Attributes do not contain user ID attribute %s.', $userIdentifierAttributeName);
+            throw new UnexpectedValueException($message);
+        }
+
+        $userIdentifierValueHashSha256 = $this->helpersManager->getHash()->getSha256($userIdentifierValue);
+
+        // Check if it already exists.
+        try {
+            $result = $this->repository->getUser($userIdentifierValueHashSha256);
+            $userId = $result->fetchOne();
+
+            if ($userId !== false) {
+                return (int)$userId;
+            }
+        } catch (Throwable $exception) {
+            $message = sprintf('Error resolving user ID. Error was: %s.', $exception->getMessage());
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+
+        // Create new
+        try {
+            $this->repository->insertUser($userIdentifierValue, $userIdentifierValueHashSha256);
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error inserting new user, however, continuing in case of race condition. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->warning($message);
+        }
+
+        // Try again, this time it should exist...
+        try {
+            $result = $this->repository->getUser($userIdentifierValueHashSha256);
+            $userIdNew = $result->fetchOne();
+
+            if ($userIdNew !== false) {
+                return (int)$userIdNew;
+            }
+
+            $message = sprintf(
+                'Error fetching user even after insertion for identifier value hash SHA256 %s.',
+                $userIdentifierValueHashSha256
+            );
+            throw new StoreException($message);
+        } catch (Throwable $exception) {
+            $message = sprintf('Error resolving user ID. Error was: %s.', $exception->getMessage());
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function resolveUserVersionId(int $userId, HashDecoratedState $hashDecoratedState): int
+    {
+        $attributeArrayHashSha256 = $hashDecoratedState->getAttributesArrayHashSha256();
+
+        // Check if it already exists.
+        try {
+            $result = $this->repository->getUserVersion($userId, $attributeArrayHashSha256);
+            $userVersionId = $result->fetchOne();
+
+            if ($userVersionId !== false) {
+                return (int)$userVersionId;
+            }
+        } catch (Throwable $exception) {
+            $message = sprintf('Error resolving user version ID. Error was: %s.', $exception->getMessage());
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+
+        // Create new
+        try {
+            $this->repository->insertUserVersion(
+                $userId,
+                serialize($hashDecoratedState->getState()->getAttributes()),
+                $attributeArrayHashSha256
+            );
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error inserting new user version, however, continuing in case of race condition. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->warning($message);
+        }
+
+        // Try again, this time it should exist...
+        try {
+            $result = $this->repository->getUserVersion($userId, $attributeArrayHashSha256);
+            $userVersionIdNew = $result->fetchOne();
+
+            if ($userVersionIdNew !== false) {
+                return (int)$userVersionIdNew;
+            }
+
+            $message = sprintf(
+                'Error fetching user version even after insertion for user ID %s.',
+                $userId
+            );
+            throw new StoreException($message);
+        } catch (Throwable $exception) {
+            $message = sprintf('Error resolving user version ID. Error was: %s.', $exception->getMessage());
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+}
diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateIdpTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateIdpTable.php
new file mode 100644
index 0000000..840cd8f
--- /dev/null
+++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateIdpTable.php
@@ -0,0 +1,87 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations;
+
+use Doctrine\DBAL\Schema\Table;
+use Doctrine\DBAL\Types\Types;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
+use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
+use Throwable;
+
+use function sprintf;
+
+class CreateIdpTable extends AbstractMigration
+{
+    protected function getLocalTablePrefix(): string
+    {
+        return 'cds_';
+    }
+
+    /**
+     * @inheritDoc
+     * @throws MigrationException
+     */
+    public function run(): void
+    {
+        $tableName = $this->preparePrefixedTableName('idp');
+
+        /** @noinspection DuplicatedCode */
+        try {
+            if ($this->schemaManager->tablesExist($tableName)) {
+                return;
+            }
+
+            $table = new Table($tableName);
+
+            $table->addColumn('id', Types::BIGINT)
+                ->setUnsigned(true)
+                ->setAutoincrement(true);
+
+            $table->addColumn('entity_id', Types::STRING)
+                ->setLength(TableConstants::COLUMN_ENTITY_ID_LENGTH);
+
+            $table->addColumn('entity_id_hash_sha256', Types::STRING)
+                ->setLength(TableConstants::COLUMN_HASH_SHA265_HEXITS_LENGTH)
+                ->setFixed(true);
+
+            $table->addColumn('metadata', Types::TEXT);
+
+            $table->addColumn('metadata_hash_sha256', Types::STRING)
+                ->setLength(TableConstants::COLUMN_HASH_SHA265_HEXITS_LENGTH)
+                ->setFixed(true);
+
+            $table->addColumn('created_at', Types::DATETIMETZ_IMMUTABLE);
+            $table->addColumn('updated_at', Types::DATETIMETZ_IMMUTABLE);
+
+            $table->setPrimaryKey(['id']);
+
+            $table->addUniqueConstraint(['entity_id_hash_sha256']);
+            $table->addIndex(['metadata_hash_sha256']);
+
+            $this->schemaManager->createTable($table);
+        } catch (Throwable $exception) {
+            throw $this->prepareGenericMigrationException(
+                sprintf('Error creating table \'%s.', $tableName),
+                $exception
+            );
+        }
+    }
+
+    /**
+     * @inheritDoc
+     * @throws MigrationException
+     */
+    public function revert(): void
+    {
+        $tableName = $this->preparePrefixedTableName('idp');
+
+        try {
+            $this->schemaManager->dropTable($tableName);
+        } catch (Throwable $exception) {
+            throw $this->prepareGenericMigrationException(sprintf('Could not drop table %s.', $tableName), $exception);
+        }
+    }
+}
diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateSpTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateSpTable.php
new file mode 100644
index 0000000..5551faf
--- /dev/null
+++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateSpTable.php
@@ -0,0 +1,87 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations;
+
+use Doctrine\DBAL\Schema\Table;
+use Doctrine\DBAL\Types\Types;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
+use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
+use Throwable;
+
+use function sprintf;
+
+class CreateSpTable extends AbstractMigration
+{
+    protected function getLocalTablePrefix(): string
+    {
+        return 'cds_';
+    }
+
+    /**
+     * @inheritDoc
+     * @throws MigrationException
+     */
+    public function run(): void
+    {
+        $tableName = $this->preparePrefixedTableName('sp');
+
+        /** @noinspection DuplicatedCode */
+        try {
+            if ($this->schemaManager->tablesExist($tableName)) {
+                return;
+            }
+
+            $table = new Table($tableName);
+
+            $table->addColumn('id', Types::BIGINT)
+                ->setUnsigned(true)
+                ->setAutoincrement(true);
+
+            $table->addColumn('entity_id', Types::STRING)
+                ->setLength(TableConstants::COLUMN_ENTITY_ID_LENGTH);
+
+            $table->addColumn('entity_id_hash_sha256', Types::STRING)
+                ->setLength(TableConstants::COLUMN_HASH_SHA265_HEXITS_LENGTH)
+                ->setFixed(true);
+
+            $table->addColumn('metadata', Types::TEXT);
+
+            $table->addColumn('metadata_hash_sha256', Types::STRING)
+                ->setLength(TableConstants::COLUMN_HASH_SHA265_HEXITS_LENGTH)
+                ->setFixed(true);
+
+            $table->addColumn('created_at', Types::DATETIMETZ_IMMUTABLE);
+            $table->addColumn('updated_at', Types::DATETIMETZ_IMMUTABLE);
+
+            $table->setPrimaryKey(['id']);
+
+            $table->addUniqueConstraint(['entity_id_hash_sha256']);
+            $table->addIndex(['metadata_hash_sha256']);
+
+            $this->schemaManager->createTable($table);
+        } catch (Throwable $exception) {
+            throw $this->prepareGenericMigrationException(
+                sprintf('Error creating table \'%s.', $tableName),
+                $exception
+            );
+        }
+    }
+
+    /**
+     * @inheritDoc
+     * @throws MigrationException
+     */
+    public function revert(): void
+    {
+        $tableName = $this->preparePrefixedTableName('sp');
+
+        try {
+            $this->schemaManager->dropTable($tableName);
+        } catch (Throwable $exception) {
+            throw $this->prepareGenericMigrationException(sprintf('Could not drop table %s.', $tableName), $exception);
+        }
+    }
+}
diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserTable.php
new file mode 100644
index 0000000..090357d
--- /dev/null
+++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserTable.php
@@ -0,0 +1,78 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations;
+
+use Doctrine\DBAL\Schema\Table;
+use Doctrine\DBAL\Types\Types;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
+use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
+use Throwable;
+
+use function sprintf;
+
+class CreateUserTable extends AbstractMigration
+{
+    protected function getLocalTablePrefix(): string
+    {
+        return 'cds_';
+    }
+
+    /**
+     * @inheritDoc
+     * @throws MigrationException
+     */
+    public function run(): void
+    {
+        $tableName = $this->preparePrefixedTableName('user');
+
+        try {
+            if ($this->schemaManager->tablesExist($tableName)) {
+                return;
+            }
+
+            $table = new Table($tableName);
+
+            $table->addColumn('id', Types::BIGINT)
+                ->setUnsigned(true)
+                ->setAutoincrement(true);
+
+            $table->addColumn('identifier', Types::TEXT)
+                ->setLength(65535);
+
+            $table->addColumn('identifier_hash_sha256', Types::STRING)
+                ->setLength(TableConstants::COLUMN_HASH_SHA265_HEXITS_LENGTH)
+                ->setFixed(true);
+
+            $table->addColumn('created_at', Types::DATETIMETZ_IMMUTABLE);
+
+            $table->setPrimaryKey(['id']);
+
+            $table->addUniqueConstraint(['identifier_hash_sha256']);
+
+            $this->schemaManager->createTable($table);
+        } catch (Throwable $exception) {
+            throw $this->prepareGenericMigrationException(
+                sprintf('Error creating table \'%s.', $tableName),
+                $exception
+            );
+        }
+    }
+
+    /**
+     * @inheritDoc
+     * @throws MigrationException
+     */
+    public function revert(): void
+    {
+        $tableName = $this->preparePrefixedTableName('user');
+
+        try {
+            $this->schemaManager->dropTable($tableName);
+        } catch (Throwable $exception) {
+            throw $this->prepareGenericMigrationException(sprintf('Could not drop table %s.', $tableName), $exception);
+        }
+    }
+}
diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserVersionTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserVersionTable.php
new file mode 100644
index 0000000..11f7926
--- /dev/null
+++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Migrations/CreateUserVersionTable.php
@@ -0,0 +1,15 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
+
+class CreateUserVersionTable extends Migrations\CreateUserVersionTable
+{
+    protected function getLocalTablePrefix(): string
+    {
+        return 'cds_';
+    }
+}
diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Repository.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Repository.php
new file mode 100644
index 0000000..7ec179c
--- /dev/null
+++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/Repository.php
@@ -0,0 +1,530 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store;
+
+use DateTimeImmutable;
+use Doctrine\DBAL\ParameterType;
+use Doctrine\DBAL\Result;
+use Doctrine\DBAL\Types\Types;
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use Throwable;
+
+class Repository
+{
+    protected Connection $connection;
+    protected LoggerInterface $logger;
+    protected string $tableNameIdp;
+    protected string $tableNameSp;
+    protected string $tableNameUser;
+    protected string $tableNameUserVersion;
+
+    public function __construct(Connection $connection, LoggerInterface $logger)
+    {
+        $this->connection = $connection;
+        $this->logger = $logger;
+
+        $this->tableNameIdp = $this->preparePrefixedTableName(TableConstants::TABLE_NAME_IDP);
+        $this->tableNameSp = $this->preparePrefixedTableName(TableConstants::TABLE_NAME_SP);
+        $this->tableNameUser = $this->preparePrefixedTableName(TableConstants::TABLE_NAME_USER);
+        $this->tableNameUserVersion = $this->preparePrefixedTableName(TableConstants::TABLE_NAME_USER_VERSION);
+    }
+
+    protected function preparePrefixedTableName(string $tableName): string
+    {
+        return $this->connection->preparePrefixedTableName(TableConstants::TABLE_PREFIX . $tableName);
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function getIdp(string $entityIdHashSha256): Result
+    {
+        try {
+            $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+            /** @psalm-suppress TooManyArguments */
+            $queryBuilder->select(
+                TableConstants::TABLE_IDP_COLUMN_NAME_ID,
+                TableConstants::TABLE_IDP_COLUMN_NAME_ENTITY_ID,
+                TableConstants::TABLE_IDP_COLUMN_NAME_ENTITY_ID_HASH_SHA256,
+                TableConstants::TABLE_IDP_COLUMN_NAME_METADATA,
+                TableConstants::TABLE_IDP_COLUMN_NAME_METADATA_HASH_SHA256,
+                TableConstants::TABLE_IDP_COLUMN_NAME_CREATED_AT,
+                TableConstants::TABLE_IDP_COLUMN_NAME_UPDATED_AT,
+            )
+                ->from($this->tableNameIdp)
+                ->where(
+                    TableConstants::TABLE_IDP_COLUMN_NAME_ENTITY_ID_HASH_SHA256 . ' = ' .
+                    $queryBuilder->createNamedParameter($entityIdHashSha256)
+                )->setMaxResults(1);
+
+            return $queryBuilder->executeQuery();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing query to get IdP by entity ID hash SHA256 \'%s\'. Error was: %s.',
+                $entityIdHashSha256,
+                $exception->getMessage()
+            );
+            $this->logger->error($message, compact('entityIdHashSha256'));
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function insertIdp(
+        string $entityId,
+        string $entityIdHashSha256,
+        string $metadata,
+        string $metadataHashSha256,
+        DateTimeImmutable $createdAt = null
+    ): void {
+        $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+        $createdAt = $createdAt ?? new DateTimeImmutable();
+
+        $queryBuilder->insert($this->tableNameIdp)
+            ->values(
+                [
+                    TableConstants::TABLE_IDP_COLUMN_NAME_ENTITY_ID => ':' .
+                        TableConstants::TABLE_IDP_COLUMN_NAME_ENTITY_ID,
+                    TableConstants::TABLE_IDP_COLUMN_NAME_ENTITY_ID_HASH_SHA256 => ':' .
+                        TableConstants::TABLE_IDP_COLUMN_NAME_ENTITY_ID_HASH_SHA256,
+                    TableConstants::TABLE_IDP_COLUMN_NAME_METADATA => ':' .
+                        TableConstants::TABLE_IDP_COLUMN_NAME_METADATA,
+                    TableConstants::TABLE_IDP_COLUMN_NAME_METADATA_HASH_SHA256 => ':' .
+                        TableConstants::TABLE_IDP_COLUMN_NAME_METADATA_HASH_SHA256,
+                    TableConstants::TABLE_IDP_COLUMN_NAME_CREATED_AT => ':' .
+                        TableConstants::TABLE_IDP_COLUMN_NAME_CREATED_AT,
+                    TableConstants::TABLE_IDP_COLUMN_NAME_UPDATED_AT => ':' .
+                        TableConstants::TABLE_IDP_COLUMN_NAME_UPDATED_AT,
+                ]
+            )
+            ->setParameters(
+                [
+                    TableConstants::TABLE_IDP_COLUMN_NAME_ENTITY_ID => $entityId,
+                    TableConstants::TABLE_IDP_COLUMN_NAME_ENTITY_ID_HASH_SHA256 => $entityIdHashSha256,
+                    TableConstants::TABLE_IDP_COLUMN_NAME_METADATA => $metadata,
+                    TableConstants::TABLE_IDP_COLUMN_NAME_METADATA_HASH_SHA256 => $metadataHashSha256,
+                    TableConstants::TABLE_IDP_COLUMN_NAME_CREATED_AT => $createdAt,
+                    TableConstants::TABLE_IDP_COLUMN_NAME_UPDATED_AT => $createdAt,
+                ],
+                [
+                    TableConstants::TABLE_IDP_COLUMN_NAME_ENTITY_ID => Types::STRING,
+                    TableConstants::TABLE_IDP_COLUMN_NAME_ENTITY_ID_HASH_SHA256 => Types::STRING,
+                    TableConstants::TABLE_IDP_COLUMN_NAME_METADATA => Types::STRING,
+                    TableConstants::TABLE_IDP_COLUMN_NAME_METADATA_HASH_SHA256 => Types::STRING,
+                    TableConstants::TABLE_IDP_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE,
+                    TableConstants::TABLE_IDP_COLUMN_NAME_UPDATED_AT => Types::DATETIMETZ_IMMUTABLE,
+                ]
+            );
+
+        try {
+            $queryBuilder->executeStatement();
+        } catch (Throwable $exception) {
+            $message = sprintf('Error executing query to insert IdP. Error was: %s.', $exception->getMessage());
+            $this->logger->error($message, compact('entityId', 'entityIdHashSha256'));
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function getSp(string $entityIdHashSha256): Result
+    {
+        try {
+            $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+            /** @psalm-suppress TooManyArguments */
+            $queryBuilder->select(
+                TableConstants::TABLE_SP_COLUMN_NAME_ID,
+                TableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID,
+                TableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID_HASH_SHA256,
+                TableConstants::TABLE_SP_COLUMN_NAME_METADATA,
+                TableConstants::TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256,
+                TableConstants::TABLE_SP_COLUMN_NAME_CREATED_AT,
+                TableConstants::TABLE_SP_COLUMN_NAME_UPDATED_AT,
+            )
+                ->from($this->tableNameSp)
+                ->where(
+                    TableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID_HASH_SHA256 . ' = ' .
+                    $queryBuilder->createNamedParameter($entityIdHashSha256)
+                )->setMaxResults(1);
+
+            return $queryBuilder->executeQuery();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing query to get SP by entity ID hash SHA256 \'%s\'. Error was: %s.',
+                $entityIdHashSha256,
+                $exception->getMessage()
+            );
+            $this->logger->error($message, compact('entityIdHashSha256'));
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function updateIdp(
+        string $idpId,
+        string $metadata,
+        string $metadataHashSha256,
+        DateTimeImmutable $updatedAt = null
+    ): void {
+        $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+        $updatedAt = $updatedAt ?? new DateTimeImmutable();
+
+        $queryBuilder->update($this->tableNameIdp)
+            ->set(
+                TableConstants::TABLE_IDP_COLUMN_NAME_METADATA,
+                ':' . TableConstants::TABLE_IDP_COLUMN_NAME_METADATA
+            )
+            ->set(
+                TableConstants::TABLE_IDP_COLUMN_NAME_METADATA_HASH_SHA256,
+                ':' . TableConstants::TABLE_IDP_COLUMN_NAME_METADATA_HASH_SHA256
+            )
+            ->set(
+                TableConstants::TABLE_IDP_COLUMN_NAME_UPDATED_AT,
+                ':' . TableConstants::TABLE_IDP_COLUMN_NAME_UPDATED_AT
+            )
+            ->setParameter(
+                TableConstants::TABLE_IDP_COLUMN_NAME_METADATA,
+                $metadata,
+                Types::STRING
+            )
+            ->setParameter(
+                TableConstants::TABLE_IDP_COLUMN_NAME_METADATA_HASH_SHA256,
+                $metadataHashSha256,
+                Types::STRING
+            )
+            ->setParameter(
+                TableConstants::TABLE_IDP_COLUMN_NAME_UPDATED_AT,
+                $updatedAt,
+                Types::DATETIMETZ_IMMUTABLE
+            )
+            ->where(
+                $queryBuilder->expr()->and(
+                    $queryBuilder->expr()->eq(
+                        TableConstants::TABLE_IDP_COLUMN_NAME_ID,
+                        $queryBuilder->createNamedParameter($idpId, Types::INTEGER)
+                    )
+                )
+            );
+
+        try {
+            $queryBuilder->executeStatement();
+        } catch (Throwable $exception) {
+            $message = sprintf('Error executing query to update IdP. Error was: %s.', $exception->getMessage());
+            $this->logger->error($message, compact('idpId', 'metadata', 'metadataHashSha256'));
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function insertSp(
+        string $entityId,
+        string $entityIdHashSha256,
+        string $metadata,
+        string $metadataHashSha256,
+        DateTimeImmutable $createdAt = null
+    ): void {
+        $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+        $createdAt = $createdAt ?? new DateTimeImmutable();
+
+        $queryBuilder->insert($this->tableNameSp)
+            ->values(
+                [
+                    TableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID => ':' .
+                        TableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID,
+                    TableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID_HASH_SHA256 => ':' .
+                        TableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID_HASH_SHA256,
+                    TableConstants::TABLE_SP_COLUMN_NAME_METADATA => ':' .
+                        TableConstants::TABLE_SP_COLUMN_NAME_METADATA,
+                    TableConstants::TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256 => ':' .
+                        TableConstants::TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256,
+                    TableConstants::TABLE_SP_COLUMN_NAME_CREATED_AT => ':' .
+                        TableConstants::TABLE_SP_COLUMN_NAME_CREATED_AT,
+                    TableConstants::TABLE_SP_COLUMN_NAME_UPDATED_AT => ':' .
+                        TableConstants::TABLE_SP_COLUMN_NAME_UPDATED_AT,
+                ]
+            )
+            ->setParameters(
+                [
+                    TableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID => $entityId,
+                    TableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID_HASH_SHA256 => $entityIdHashSha256,
+                    TableConstants::TABLE_SP_COLUMN_NAME_METADATA => $metadata,
+                    TableConstants::TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256 => $metadataHashSha256,
+                    TableConstants::TABLE_SP_COLUMN_NAME_CREATED_AT => $createdAt,
+                    TableConstants::TABLE_SP_COLUMN_NAME_UPDATED_AT => $createdAt,
+                ],
+                [
+                    TableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID => Types::STRING,
+                    TableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID_HASH_SHA256 => Types::STRING,
+                    TableConstants::TABLE_SP_COLUMN_NAME_METADATA => Types::STRING,
+                    TableConstants::TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256 => Types::STRING,
+                    TableConstants::TABLE_SP_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE,
+                    TableConstants::TABLE_SP_COLUMN_NAME_UPDATED_AT => Types::DATETIMETZ_IMMUTABLE,
+                ]
+            );
+
+        try {
+            $queryBuilder->executeStatement();
+        } catch (Throwable $exception) {
+            $message = sprintf('Error executing query to insert SP. Error was: %s.', $exception->getMessage());
+            $this->logger->error($message, compact('entityId', 'entityIdHashSha256'));
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function updateSp(
+        int $spId,
+        string $metadata,
+        string $metadataHashSha256,
+        DateTimeImmutable $updatedAt = null
+    ): void {
+        $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+        $updatedAt = $updatedAt ?? new DateTimeImmutable();
+
+        $queryBuilder->update($this->tableNameSp)
+            ->set(
+                TableConstants::TABLE_SP_COLUMN_NAME_METADATA,
+                ':' . TableConstants::TABLE_SP_COLUMN_NAME_METADATA
+            )
+            ->set(
+                TableConstants::TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256,
+                ':' . TableConstants::TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256
+            )
+            ->set(
+                TableConstants::TABLE_SP_COLUMN_NAME_UPDATED_AT,
+                ':' . TableConstants::TABLE_SP_COLUMN_NAME_UPDATED_AT
+            )
+            ->setParameter(
+                TableConstants::TABLE_SP_COLUMN_NAME_METADATA,
+                $metadata,
+                Types::STRING
+            )
+            ->setParameter(
+                TableConstants::TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256,
+                $metadataHashSha256,
+                Types::STRING
+            )
+            ->setParameter(
+                TableConstants::TABLE_SP_COLUMN_NAME_UPDATED_AT,
+                $updatedAt,
+                Types::DATETIMETZ_IMMUTABLE
+            )
+            ->where(
+                $queryBuilder->expr()->and(
+                    $queryBuilder->expr()->eq(
+                        TableConstants::TABLE_SP_COLUMN_NAME_ID,
+                        $queryBuilder->createNamedParameter($spId, Types::INTEGER)
+                    )
+                )
+            );
+
+        try {
+            $queryBuilder->executeStatement();
+        } catch (Throwable $exception) {
+            $message = sprintf('Error executing query to update SP. Error was: %s.', $exception->getMessage());
+            $this->logger->error($message, compact('spId', 'metadata', 'metadataHashSha256'));
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function getUser(string $identifierHashSha256): Result
+    {
+        try {
+            $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+            /** @psalm-suppress TooManyArguments */
+            $queryBuilder->select(
+                TableConstants::TABLE_USER_COLUMN_NAME_ID,
+                TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER,
+                TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256,
+                TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT,
+            )
+                ->from($this->tableNameUser)
+                ->where(
+                    $queryBuilder->expr()->and(
+                        $queryBuilder->expr()->eq(
+                            TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256,
+                            $queryBuilder->createNamedParameter($identifierHashSha256)
+                        )
+                    )
+                )->setMaxResults(1);
+
+            return $queryBuilder->executeQuery();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing query to get user. Error was: %s.',
+                $identifierHashSha256,
+                $exception->getMessage()
+            );
+            $this->logger->error($message, compact('identifierHashSha256'));
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function insertUser(
+        string $identifier,
+        string $identifierHashSha256,
+        DateTimeImmutable $createdAt = null
+    ): void {
+        $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+        $createdAt = $createdAt ?? new DateTimeImmutable();
+
+        $queryBuilder->insert($this->tableNameUser)
+            ->values(
+                [
+                    TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER => ':' .
+                        TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER,
+                    TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 => ':' .
+                        TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256,
+                    TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT => ':' .
+                        TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT,
+                ]
+            )
+            ->setParameters(
+                [
+                    TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER => $identifier,
+                    TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 => $identifierHashSha256,
+                    TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT => $createdAt,
+                ],
+                [
+                    TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER => Types::TEXT,
+                    TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 => Types::STRING,
+                    TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE
+                ]
+            );
+
+        try {
+            $queryBuilder->executeStatement();
+        } catch (Throwable $exception) {
+            $message = sprintf('Error executing query to insert user. Error was: %s.', $exception->getMessage());
+            $this->logger->error(
+                $message,
+                compact('identifier', 'identifierHashSha256')
+            );
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+
+    /**
+     * @throws StoreException
+     */
+    public function getUserVersion(int $userId, string $attributesHashSha256): Result
+    {
+        try {
+            $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+            /** @psalm-suppress TooManyArguments */
+            $queryBuilder->select(
+                TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID,
+                TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID,
+                TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES,
+                TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256,
+                TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT,
+            )
+                ->from($this->tableNameUserVersion)
+                ->where(
+                    $queryBuilder->expr()->and(
+                        $queryBuilder->expr()->eq(
+                            TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID,
+                            $queryBuilder->createNamedParameter($userId, ParameterType::INTEGER)
+                        ),
+                        $queryBuilder->expr()->eq(
+                            TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256,
+                            $queryBuilder->createNamedParameter($attributesHashSha256)
+                        )
+                    )
+                )->setMaxResults(1);
+
+            return $queryBuilder->executeQuery();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing query to get user version for user ID %s and attribute array hash %s. Error was: %s.',
+                $userId,
+                $attributesHashSha256,
+                $exception->getMessage()
+            );
+            $this->logger->error($message, compact('userId', 'attributesHashSha256'));
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function insertUserVersion(
+        int $userId,
+        string $attributes,
+        string $attributesHashSha256,
+        DateTimeImmutable $createdAt = null
+    ): void {
+        $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+        $createdAt = $createdAt ?? new DateTimeImmutable();
+
+        $queryBuilder->insert($this->tableNameUserVersion)
+            ->values(
+                [
+                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID => ':' .
+                        TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID,
+                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES => ':' .
+                        TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES,
+                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256 => ':' .
+                        TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256,
+                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT => ':' .
+                        TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT,
+                ]
+            )
+            ->setParameters(
+                [
+                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID => $userId,
+                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES => $attributes,
+                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256 => $attributesHashSha256,
+                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT => $createdAt,
+                ],
+                [
+                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID => Types::BIGINT,
+                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES => Types::TEXT,
+                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256 => Types::STRING,
+                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE,
+                ]
+            );
+
+        try {
+            $queryBuilder->executeStatement();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing query to insert user version. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->error($message, compact('userId', 'attributesHashSha256'));
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+}
diff --git a/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/TableConstants.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/TableConstants.php
new file mode 100644
index 0000000..40b45a0
--- /dev/null
+++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Current/Store/TableConstants.php
@@ -0,0 +1,52 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\TableConstants as BaseTableConstants;
+
+class TableConstants extends BaseTableConstants
+{
+    public const TABLE_PREFIX = 'cds_'; // current data store
+
+    // Table 'idp'
+    public const TABLE_NAME_IDP = 'idp';
+    public const TABLE_ALIAS_IDP = self::TABLE_PREFIX . 'i';
+    public const TABLE_IDP_COLUMN_NAME_ID = 'id'; // int
+    public const TABLE_IDP_COLUMN_NAME_ENTITY_ID = 'entity_id'; // Entity ID value, string, varchar(1024)
+    public const TABLE_IDP_COLUMN_NAME_ENTITY_ID_HASH_SHA256 = 'entity_id_hash_sha256'; // ha256 hash hexits, char(64)
+    public const TABLE_IDP_COLUMN_NAME_METADATA = 'metadata'; // Serialized IdP metadata version
+    public const TABLE_IDP_COLUMN_NAME_METADATA_HASH_SHA256 = 'metadata_hash_sha256'; // Metadata sha256 hash
+    public const TABLE_IDP_COLUMN_NAME_CREATED_AT = 'created_at'; // First time IdP usage, datetime
+    public const TABLE_IDP_COLUMN_NAME_UPDATED_AT = 'updated_at'; // First time IdP usage, datetime
+
+    // Table 'sp', same structure as in 'idp'
+    public const TABLE_NAME_SP = 'sp';
+    public const TABLE_ALIAS_SP = self::TABLE_PREFIX . 's';
+    public const TABLE_SP_COLUMN_NAME_ID = 'id';
+    public const TABLE_SP_COLUMN_NAME_ENTITY_ID = 'entity_id';
+    public const TABLE_SP_COLUMN_NAME_ENTITY_ID_HASH_SHA256 = 'entity_id_hash_sha256';
+    public const TABLE_SP_COLUMN_NAME_METADATA = 'metadata';
+    public const TABLE_SP_COLUMN_NAME_METADATA_HASH_SHA256 = 'metadata_hash_sha256';
+    public const TABLE_SP_COLUMN_NAME_CREATED_AT = 'created_at';
+    public const TABLE_SP_COLUMN_NAME_UPDATED_AT = 'updated_at';
+
+    // Table 'user'
+    public const TABLE_NAME_USER = 'user';
+    public const TABLE_ALIAS_USER = self::TABLE_PREFIX . 'u';
+    public const TABLE_USER_COLUMN_NAME_ID = 'id'; // int
+    public const TABLE_USER_COLUMN_NAME_IDENTIFIER = 'identifier'; // text, varies... (can be ePTID, which is long XML).
+    public const TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 = 'identifier_hash_sha256';
+    public const TABLE_USER_COLUMN_NAME_CREATED_AT = 'created_at';
+
+    // Table 'user_version' (versioned attributes)
+    public const TABLE_NAME_USER_VERSION = 'user_version';
+    public const TABLE_ALIAS_USER_VERSION = self::TABLE_PREFIX . 'uv';
+    public const TABLE_ALIAS_USER_VERSION_2 = self::TABLE_ALIAS_USER_VERSION . '_2';
+    public const TABLE_USER_VERSION_COLUMN_NAME_ID = 'id'; // int ID
+    public const TABLE_USER_VERSION_COLUMN_NAME_USER_ID = 'user_id'; // FK
+    public const TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES = 'attributes'; // Serialized attributes version
+    public const TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256 = 'attributes_hash_sha256';
+    public const TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT = 'created_at';
+}
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store.php
similarity index 70%
rename from src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store.php
rename to src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store.php
index dbee374..6c3214b 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store.php
+++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store.php
@@ -2,31 +2,24 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned;
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned;
 
-use DateTimeImmutable;
 use Psr\Log\LoggerInterface;
-use SimpleSAML\Module\accounting\Entities\Activity;
-use SimpleSAML\Module\accounting\Entities\Authentication\Event;
-use SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider;
-use SimpleSAML\Module\accounting\Entities\User;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState;
+use SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory;
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\StoreInterface;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\HelpersManager;
-use SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractStore;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\HashDecoratedState;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\RawActivity;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\RawConnectedServiceProvider;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Repository;
-use SimpleSAML\Module\accounting\Stores\Interfaces\DataStoreInterface;
 use Throwable;
 
-class Store extends AbstractStore implements DataStoreInterface
+class Store extends AbstractStore implements StoreInterface
 {
-    protected Repository $repository;
     protected HelpersManager $helpersManager;
+    private Repository $repository;
 
     /**
      * @throws StoreException
@@ -37,13 +30,13 @@ class Store extends AbstractStore implements DataStoreInterface
         string $connectionKey = null,
         string $connectionType = ModuleConfiguration\ConnectionType::MASTER,
         Factory $connectionFactory = null,
-        Repository $repository = null,
-        HelpersManager $helpersManager = null
+        HelpersManager $helpersManager = null,
+        Repository $repository = null
     ) {
         parent::__construct($moduleConfiguration, $logger, $connectionKey, $connectionType, $connectionFactory);
 
-        $this->repository = $repository ?? new Repository($this->connection, $this->logger);
         $this->helpersManager = $helpersManager ?? new HelpersManager();
+        $this->repository = $repository ?? new Repository($this->connection, $this->logger);
     }
 
     /**
@@ -67,30 +60,7 @@ class Store extends AbstractStore implements DataStoreInterface
     /**
      * @throws StoreException
      */
-    public function persist(Event $authenticationEvent): void
-    {
-        $hashDecoratedState = new HashDecoratedState($authenticationEvent->getState());
-
-        $idpId = $this->resolveIdpId($hashDecoratedState);
-        $idpVersionId = $this->resolveIdpVersionId($idpId, $hashDecoratedState);
-        $spId = $this->resolveSpId($hashDecoratedState);
-        $spVersionId = $this->resolveSpVersionId($spId, $hashDecoratedState);
-        $userId = $this->resolveUserId($hashDecoratedState);
-        $userVersionId = $this->resolveUserVersionId($userId, $hashDecoratedState);
-        $idpSpUserVersionId = $this->resolveIdpSpUserVersionId($idpVersionId, $spVersionId, $userVersionId);
-
-        $this->repository->insertAuthenticationEvent(
-            $idpSpUserVersionId,
-            $authenticationEvent->getHappenedAt(),
-            $authenticationEvent->getState()->getClientIpAddress(),
-            $authenticationEvent->getState()->getAuthenticationProtocol()->getDesignation()
-        );
-    }
-
-    /**
-     * @throws StoreException
-     */
-    protected function resolveIdpId(HashDecoratedState $hashDecoratedState): int
+    public function resolveIdpId(HashDecoratedState $hashDecoratedState): int
     {
         $idpEntityIdHashSha256 = $hashDecoratedState->getIdentityProviderEntityIdHashSha256();
 
@@ -144,7 +114,7 @@ class Store extends AbstractStore implements DataStoreInterface
     /**
      * @throws StoreException
      */
-    protected function resolveIdpVersionId(int $idpId, HashDecoratedState $hashDecoratedState): int
+    public function resolveIdpVersionId(int $idpId, HashDecoratedState $hashDecoratedState): int
     {
         // Check if it already exists.
         $idpMetadataArrayHashSha256 = $hashDecoratedState->getIdentityProviderMetadataArrayHashSha256();
@@ -199,7 +169,7 @@ class Store extends AbstractStore implements DataStoreInterface
     /**
      * @throws StoreException
      */
-    protected function resolveSpId(HashDecoratedState $hashDecoratedState): int
+    public function resolveSpId(HashDecoratedState $hashDecoratedState): int
     {
         $spEntityIdHashSha256 = $hashDecoratedState->getServiceProviderEntityIdHashSha256();
 
@@ -253,7 +223,7 @@ class Store extends AbstractStore implements DataStoreInterface
     /**
      * @throws StoreException
      */
-    protected function resolveSpVersionId(int $spId, HashDecoratedState $hashDecoratedState): int
+    public function resolveSpVersionId(int $spId, HashDecoratedState $hashDecoratedState): int
     {
         // Check if it already exists.
         $spMetadataArrayHashSha256 = $hashDecoratedState->getServiceProviderMetadataArrayHashSha256();
@@ -308,7 +278,7 @@ class Store extends AbstractStore implements DataStoreInterface
     /**
      * @throws StoreException
      */
-    protected function resolveUserId(HashDecoratedState $hashDecoratedState): int
+    public function resolveUserId(HashDecoratedState $hashDecoratedState): int
     {
         $userIdentifierAttributeName = $this->moduleConfiguration->getUserIdAttributeName();
 
@@ -367,7 +337,7 @@ class Store extends AbstractStore implements DataStoreInterface
     /**
      * @throws StoreException
      */
-    protected function resolveUserVersionId(int $userId, HashDecoratedState $hashDecoratedState): int
+    public function resolveUserVersionId(int $userId, HashDecoratedState $hashDecoratedState): int
     {
         $attributeArrayHashSha256 = $hashDecoratedState->getAttributesArrayHashSha256();
 
@@ -422,7 +392,7 @@ class Store extends AbstractStore implements DataStoreInterface
     /**
      * @throws StoreException
      */
-    protected function resolveIdpSpUserVersionId(int $idpVersionId, int $spVersionId, int $userVersionId): int
+    public function resolveIdpSpUserVersionId(int $idpVersionId, int $spVersionId, int $userVersionId): int
     {
         // Check if it already exists.
         try {
@@ -471,103 +441,4 @@ class Store extends AbstractStore implements DataStoreInterface
             throw new StoreException($message, (int)$exception->getCode(), $exception);
         }
     }
-
-    /**
-     * @throws StoreException
-     */
-    public function getConnectedOrganizations(string $userIdentifierHashSha256): ConnectedServiceProvider\Bag
-    {
-        $connectedServiceProviderBag = new ConnectedServiceProvider\Bag();
-
-        $results = $this->repository->getConnectedServiceProviders($userIdentifierHashSha256);
-
-        if (empty($results)) {
-            return $connectedServiceProviderBag;
-        }
-
-        try {
-            $databasePlatform = $this->connection->dbal()->getDatabasePlatform();
-
-            /** @var array $result */
-            foreach ($results as $result) {
-                $rawConnectedServiceProvider = new RawConnectedServiceProvider($result, $databasePlatform);
-
-                $serviceProvider = $this->helpersManager
-                    ->getProviderResolver()
-                    ->forServiceFromMetadataArray($rawConnectedServiceProvider->getServiceProviderMetadata());
-                $user = new User($rawConnectedServiceProvider->getUserAttributes());
-
-                $connectedServiceProviderBag->addOrReplace(
-                    new ConnectedServiceProvider(
-                        $serviceProvider,
-                        $rawConnectedServiceProvider->getNumberOfAuthentications(),
-                        $rawConnectedServiceProvider->getLastAuthenticationAt(),
-                        $rawConnectedServiceProvider->getFirstAuthenticationAt(),
-                        $user
-                    )
-                );
-            }
-        } catch (Throwable $exception) {
-            $message = sprintf(
-                'Error populating connected service provider bag. Error was: %s',
-                $exception->getMessage()
-            );
-            throw new StoreException($message, (int)$exception->getCode(), $exception);
-        }
-
-        return $connectedServiceProviderBag;
-    }
-
-
-    /**
-     * @throws StoreException
-     */
-    public function getActivity(string $userIdentifierHashSha256, int $maxResults, int $firstResult): Activity\Bag
-    {
-        $results =  $this->repository->getActivity($userIdentifierHashSha256, $maxResults, $firstResult);
-
-        $activityBag = new Activity\Bag();
-
-        if (empty($results)) {
-            return $activityBag;
-        }
-
-        try {
-            /** @var array $result */
-            foreach ($results as $result) {
-                $rawActivity = new RawActivity($result, $this->connection->dbal()->getDatabasePlatform());
-                $serviceProvider = $this->helpersManager
-                    ->getProviderResolver()
-                    ->forServiceFromMetadataArray($rawActivity->getServiceProviderMetadata());
-                $user = new User($rawActivity->getUserAttributes());
-
-                $activityBag->add(
-                    new Activity(
-                        $serviceProvider,
-                        $user,
-                        $rawActivity->getHappenedAt(),
-                        $rawActivity->getClientIpAddress(),
-                        $rawActivity->getAuthenticationProtocolDesignation()
-                    )
-                );
-            }
-        } catch (Throwable $exception) {
-            $message = sprintf(
-                'Error populating activity bag. Error was: %s',
-                $exception->getMessage()
-            );
-            throw new StoreException($message, (int)$exception->getCode(), $exception);
-        }
-
-        return $activityBag;
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public function deleteDataOlderThan(DateTimeImmutable $dateTime): void
-    {
-        // Only delete authentication events. Versioned data (IdP / SP metadata, user attributes) remain.
-        $this->repository->deleteAuthenticationEventsOlderThan($dateTime);
-    }
 }
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpSpUserVersionTable.php
similarity index 87%
rename from src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTable.php
rename to src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpSpUserVersionTable.php
index 5f23e4e..dd05d1d 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTable.php
+++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpSpUserVersionTable.php
@@ -2,17 +2,17 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
 
 use Doctrine\DBAL\Schema\Table;
 use Doctrine\DBAL\Types\Types;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
 use Throwable;
 
 use function sprintf;
 
-class Version20220801000600CreateIdpSpUserVersionTable extends AbstractMigration
+class CreateIdpSpUserVersionTable extends AbstractMigration
 {
     protected function getLocalTablePrefix(): string
     {
@@ -28,6 +28,10 @@ class Version20220801000600CreateIdpSpUserVersionTable extends AbstractMigration
         $tableName = $this->preparePrefixedTableName('idp_sp_user_version');
 
         try {
+            if ($this->schemaManager->tablesExist($tableName)) {
+                return;
+            }
+
             $table = new Table($tableName);
 
             $table->addColumn('id', Types::BIGINT)
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpTable.php
similarity index 80%
rename from src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTable.php
rename to src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpTable.php
index 4bddd2a..7a37729 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTable.php
+++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpTable.php
@@ -2,18 +2,18 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
 
 use Doctrine\DBAL\Schema\Table;
 use Doctrine\DBAL\Types\Types;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\TableConstants;
 use Throwable;
 
 use function sprintf;
 
-class Version20220801000000CreateIdpTable extends AbstractMigration
+class CreateIdpTable extends AbstractMigration
 {
     protected function getLocalTablePrefix(): string
     {
@@ -30,6 +30,10 @@ class Version20220801000000CreateIdpTable extends AbstractMigration
 
         /** @noinspection DuplicatedCode */
         try {
+            if ($this->schemaManager->tablesExist($tableName)) {
+                return;
+            }
+
             $table = new Table($tableName);
 
             $table->addColumn('id', Types::BIGINT)
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpVersionTable.php
similarity index 80%
rename from src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTable.php
rename to src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpVersionTable.php
index 1151b3a..fdbd7c5 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTable.php
+++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpVersionTable.php
@@ -2,16 +2,16 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
 
 use Doctrine\DBAL\Schema\Table;
 use Doctrine\DBAL\Types\Types;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\TableConstants;
 use Throwable;
 
-class Version20220801000100CreateIdpVersionTable extends AbstractMigration
+class CreateIdpVersionTable extends AbstractMigration
 {
     protected function getLocalTablePrefix(): string
     {
@@ -27,6 +27,10 @@ class Version20220801000100CreateIdpVersionTable extends AbstractMigration
         $tableName = $this->preparePrefixedTableName('idp_version');
 
         try {
+            if ($this->schemaManager->tablesExist($tableName)) {
+                return;
+            }
+
             $table = new Table($tableName);
 
             $table->addColumn('id', Types::BIGINT)
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpTable.php
similarity index 80%
rename from src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTable.php
rename to src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpTable.php
index bee93ca..a168bdf 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTable.php
+++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpTable.php
@@ -2,18 +2,18 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
 
 use Doctrine\DBAL\Schema\Table;
 use Doctrine\DBAL\Types\Types;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\TableConstants;
 use Throwable;
 
 use function sprintf;
 
-class Version20220801000200CreateSpTable extends AbstractMigration
+class CreateSpTable extends AbstractMigration
 {
     protected function getLocalTablePrefix(): string
     {
@@ -30,6 +30,10 @@ class Version20220801000200CreateSpTable extends AbstractMigration
 
         /** @noinspection DuplicatedCode */
         try {
+            if ($this->schemaManager->tablesExist($tableName)) {
+                return;
+            }
+
             $table = new Table($tableName);
 
             $table->addColumn('id', Types::BIGINT)
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpVersionTable.php
similarity index 81%
rename from src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTable.php
rename to src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpVersionTable.php
index e2e0028..6f7a4c0 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTable.php
+++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpVersionTable.php
@@ -2,18 +2,18 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
 
 use Doctrine\DBAL\Schema\Table;
 use Doctrine\DBAL\Types\Types;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\TableConstants;
 use Throwable;
 
 use function sprintf;
 
-class Version20220801000300CreateSpVersionTable extends AbstractMigration
+class CreateSpVersionTable extends AbstractMigration
 {
     protected function getLocalTablePrefix(): string
     {
@@ -29,6 +29,10 @@ class Version20220801000300CreateSpVersionTable extends AbstractMigration
         $tableName = $this->preparePrefixedTableName('sp_version');
 
         try {
+            if ($this->schemaManager->tablesExist($tableName)) {
+                return;
+            }
+
             $table = new Table($tableName);
 
             $table->addColumn('id', Types::BIGINT)
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserTable.php
similarity index 79%
rename from src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTable.php
rename to src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserTable.php
index ff14736..511bf5b 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTable.php
+++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserTable.php
@@ -2,18 +2,18 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
 
 use Doctrine\DBAL\Schema\Table;
 use Doctrine\DBAL\Types\Types;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\TableConstants;
 use Throwable;
 
 use function sprintf;
 
-class Version20220801000400CreateUserTable extends AbstractMigration
+class CreateUserTable extends AbstractMigration
 {
     protected function getLocalTablePrefix(): string
     {
@@ -29,6 +29,10 @@ class Version20220801000400CreateUserTable extends AbstractMigration
         $tableName = $this->preparePrefixedTableName('user');
 
         try {
+            if ($this->schemaManager->tablesExist($tableName)) {
+                return;
+            }
+
             $table = new Table($tableName);
 
             $table->addColumn('id', Types::BIGINT)
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTable.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserVersionTable.php
similarity index 81%
rename from src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTable.php
rename to src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserVersionTable.php
index a351c6a..54b2693 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTable.php
+++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserVersionTable.php
@@ -2,18 +2,18 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
 
 use Doctrine\DBAL\Schema\Table;
 use Doctrine\DBAL\Types\Types;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\TableConstants;
 use Throwable;
 
 use function sprintf;
 
-class Version20220801000500CreateUserVersionTable extends AbstractMigration
+class CreateUserVersionTable extends AbstractMigration
 {
     protected function getLocalTablePrefix(): string
     {
@@ -29,6 +29,10 @@ class Version20220801000500CreateUserVersionTable extends AbstractMigration
         $tableName = $this->preparePrefixedTableName('user_version');
 
         try {
+            if ($this->schemaManager->tablesExist($tableName)) {
+                return;
+            }
+
             $table = new Table($tableName);
 
             $table->addColumn('id', Types::BIGINT)
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Repository.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Repository.php
similarity index 54%
rename from src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Repository.php
rename to src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Repository.php
index 239adaf..0284c4e 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Repository.php
+++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Repository.php
@@ -2,15 +2,15 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store;
 
 use DateTimeImmutable;
 use Doctrine\DBAL\ParameterType;
 use Doctrine\DBAL\Result;
 use Doctrine\DBAL\Types\Types;
 use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
 use Throwable;
 
 class Repository
@@ -24,7 +24,6 @@ class Repository
     protected string $tableNameUser;
     protected string $tableNameUserVersion;
     protected string $tableNameIdpSpUserVersion;
-    protected string $tableNameAuthenticationEvent;
 
     public function __construct(Connection $connection, LoggerInterface $logger)
     {
@@ -37,10 +36,9 @@ class Repository
         $this->tableNameSpVersion = $this->preparePrefixedTableName(TableConstants::TABLE_NAME_SP_VERSION);
         $this->tableNameUser = $this->preparePrefixedTableName(TableConstants::TABLE_NAME_USER);
         $this->tableNameUserVersion = $this->preparePrefixedTableName(TableConstants::TABLE_NAME_USER_VERSION);
-        $this->tableNameIdpSpUserVersion =
-            $this->preparePrefixedTableName(TableConstants::TABLE_NAME_IDP_SP_USER_VERSION);
-        $this->tableNameAuthenticationEvent =
-            $this->preparePrefixedTableName(TableConstants::TABLE_NAME_AUTHENTICATION_EVENT);
+        $this->tableNameIdpSpUserVersion = $this->preparePrefixedTableName(
+            TableConstants::TABLE_NAME_IDP_SP_USER_VERSION
+        );
     }
 
     protected function preparePrefixedTableName(string $tableName): string
@@ -665,461 +663,4 @@ class Repository
             throw new StoreException($message, (int)$exception->getCode(), $exception);
         }
     }
-
-    /**
-     * @throws StoreException
-     */
-    public function insertAuthenticationEvent(
-        int $idpSpUserVersionId,
-        DateTimeImmutable $happenedAt,
-        string $clientIpAddress = null,
-        string $authenticationProtocolDesignation = null,
-        DateTimeImmutable $createdAt = null
-    ): void {
-        try {
-            $queryBuilder = $this->connection->dbal()->createQueryBuilder();
-
-            $createdAt = $createdAt ?? new DateTimeImmutable();
-
-            $queryBuilder->insert($this->tableNameAuthenticationEvent)
-                ->values(
-                    [
-                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_IDP_SP_USER_VERSION_ID => ':' .
-                            TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_IDP_SP_USER_VERSION_ID,
-                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT => ':' .
-                            TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT,
-                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS => ':' .
-                            TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS,
-                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION =>
-                            ':' .
-                            TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION,
-                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT => ':' .
-                            TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT,
-                    ]
-                )
-                ->setParameters(
-                    [
-                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_IDP_SP_USER_VERSION_ID =>
-                            $idpSpUserVersionId,
-                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT => $happenedAt,
-                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS => $clientIpAddress,
-                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION =>
-                            $authenticationProtocolDesignation,
-                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT => $createdAt,
-                    ],
-                    [
-                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_IDP_SP_USER_VERSION_ID =>
-                            Types::BIGINT,
-                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT =>
-                            Types::DATETIMETZ_IMMUTABLE,
-                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS =>
-                            Types::STRING,
-                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION =>
-                            Types::STRING,
-                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT =>
-                            Types::DATETIMETZ_IMMUTABLE,
-                    ]
-                );
-
-            $queryBuilder->executeStatement();
-        } catch (Throwable $exception) {
-            $message = sprintf(
-                'Error executing query to insert AuthenticationEvent. Error was: %s.',
-                $exception->getMessage()
-            );
-            $this->logger->error($message, compact('idpSpUserVersionId'));
-            throw new StoreException($message, (int)$exception->getCode(), $exception);
-        }
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public function getConnectedServiceProviders(string $userIdentifierHashSha256): array
-    {
-        try {
-            $authenticationEventsQueryBuilder = $this->connection->dbal()->createQueryBuilder();
-            $lastMetadataAndAttributesQueryBuilder = $this->connection->dbal()->createQueryBuilder();
-
-            /** @psalm-suppress TooManyArguments */
-            $authenticationEventsQueryBuilder->select(
-                //'vs.entity_id AS sp_entity_id',
-                TableConstants::TABLE_ALIAS_SP . '.' .
-                TableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID . ' AS ' .
-                TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_SP_ENTITY_ID,
-                //'COUNT(vae.id) AS number_of_authentications',
-                'COUNT(' .  TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' .
-                TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_ID . ') AS ' .
-                TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS,
-                //'MAX(vae.happened_at) AS last_authentication_at',
-                'MAX(' .  TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' .
-                TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT . ') AS ' .
-                TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_LAST_AUTHENTICATION_AT,
-                //'MIN(vae.happened_at) AS first_authentication_at',
-                'MIN(' .  TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' .
-                TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT . ') AS ' .
-                TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_FIRST_AUTHENTICATION_AT,
-            )->from($this->tableNameAuthenticationEvent, TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT)
-                ->leftJoin(
-                    //'vae',
-                    TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT,
-                    //'vds_idp_sp_user_version',
-                    $this->tableNameIdpSpUserVersion,
-                    //'visuv',
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
-                    //'vae.idp_sp_user_version_id = visuv.id'
-                    TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' .
-                    TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_IDP_SP_USER_VERSION_ID . ' = ' .
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
-                    TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID
-                )
-                ->leftJoin(
-                    //'visuv',
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
-                    //'vds_sp_version',
-                    $this->tableNameSpVersion,
-                    //'vsv',
-                    TableConstants::TABLE_ALIAS_SP_VERSION,
-                    //'visuv.sp_version_id = vsv.id'
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
-                    TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID . ' = ' .
-                    TableConstants::TABLE_ALIAS_SP_VERSION . '.' . TableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID
-                )
-                ->leftJoin(
-                    //'vsv',
-                    TableConstants::TABLE_ALIAS_SP_VERSION,
-                    //'vds_sp',
-                    $this->tableNameSp,
-                    //'vs',
-                    TableConstants::TABLE_ALIAS_SP,
-                    //'vsv.sp_id = vs.id'
-                    TableConstants::TABLE_ALIAS_SP_VERSION . '.' .
-                    TableConstants::TABLE_SP_VERSION_COLUMN_NAME_SP_ID . ' = ' .
-                    TableConstants::TABLE_ALIAS_SP . '.' . TableConstants::TABLE_SP_COLUMN_NAME_ID
-                )
-                ->leftJoin(
-                    //'visuv',
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
-                    //'vds_user_version',
-                    $this->tableNameUserVersion,
-                    //'vuv',
-                    TableConstants::TABLE_ALIAS_USER_VERSION,
-                    //'visuv.user_version_id = vuv.id'
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
-                    TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID . ' = ' .
-                    TableConstants::TABLE_ALIAS_USER_VERSION . '.' . TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID
-                )
-                ->leftJoin(
-                    //'vuv',
-                    TableConstants::TABLE_ALIAS_USER_VERSION,
-                    //'vds_user',
-                    $this->tableNameUser,
-                    //'vu',
-                    TableConstants::TABLE_ALIAS_USER,
-                    //'vuv.user_id = vu.id'
-                    TableConstants::TABLE_ALIAS_USER_VERSION . '.' .
-                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID . ' = ' .
-                    TableConstants::TABLE_ALIAS_USER . '.' . TableConstants::TABLE_USER_COLUMN_NAME_ID
-                )
-                ->where(
-                    //'vu.identifier_hash_sha256 = ' .
-                    TableConstants::TABLE_ALIAS_USER . '.' .
-                    TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 . ' = ' .
-                    $authenticationEventsQueryBuilder->createNamedParameter($userIdentifierHashSha256)
-                )
-                ->groupBy(
-                    //'vs.id'
-                    TableConstants::TABLE_ALIAS_SP . '.' . TableConstants::TABLE_SP_COLUMN_NAME_ID
-                )
-                ->orderBy(
-                    //'number_of_authentications',
-                    TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS,
-                    'DESC'
-                );
-
-
-            /** @psalm-suppress TooManyArguments */
-            $lastMetadataAndAttributesQueryBuilder->select(
-                //'vs.entity_id AS sp_entity_id',
-                TableConstants::TABLE_ALIAS_SP . '.' . TableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID . ' AS ' .
-                TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_SP_ENTITY_ID,
-                //'vsv.metadata AS sp_metadata',
-                TableConstants::TABLE_ALIAS_SP_VERSION . '.' . TableConstants::TABLE_SP_VERSION_COLUMN_NAME_METADATA .
-                ' AS ' . TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_SP_METADATA,
-                //'vuv.attributes AS user_attributes',
-                TableConstants::TABLE_ALIAS_USER_VERSION . '.' .
-                TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES . ' AS ' .
-                TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_USER_ATTRIBUTES
-                //            'vsv.id AS sp_version_id',
-                //            'vuv.id AS user_version_id',
-            )->from(
-                //'vds_authentication_event',
-                $this->tableNameAuthenticationEvent,
-                //'vae'
-                TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT
-            )
-                ->leftJoin(
-                    //'vae',
-                    TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT,
-                    //'vds_idp_sp_user_version',
-                    $this->tableNameIdpSpUserVersion,
-                    //'visuv',
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
-                    //'vae.idp_sp_user_version_id = visuv.id'
-                    TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' .
-                    TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_IDP_SP_USER_VERSION_ID . ' = ' .
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
-                    TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID
-                )
-                ->leftJoin(
-                    //'visuv',
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
-                    //'vds_sp_version',
-                    $this->tableNameSpVersion,
-                    //'vsv',
-                    TableConstants::TABLE_ALIAS_SP_VERSION,
-                    //'visuv.sp_version_id = vsv.id'
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
-                    TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID . ' = ' .
-                    TableConstants::TABLE_ALIAS_SP_VERSION . '.' . TableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID
-                )
-                ->leftJoin(
-                    //'vsv',
-                    TableConstants::TABLE_ALIAS_SP_VERSION,
-                    //'vds_sp',
-                    $this->tableNameSp,
-                    //'vs',
-                    TableConstants::TABLE_ALIAS_SP,
-                    //'vsv.sp_id = vs.id'
-                    TableConstants::TABLE_ALIAS_SP_VERSION . '.' .
-                    TableConstants::TABLE_SP_VERSION_COLUMN_NAME_SP_ID . ' = ' . TableConstants::TABLE_ALIAS_SP . '.' .
-                    TableConstants::TABLE_SP_COLUMN_NAME_ID
-                )
-                ->leftJoin(
-                    //'visuv',
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
-                    //'vds_user_version',
-                    $this->tableNameUserVersion,
-                    //'vuv',
-                    TableConstants::TABLE_ALIAS_USER_VERSION,
-                    //'visuv.user_version_id = vuv.id'
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
-                    TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID . ' = ' .
-                    TableConstants::TABLE_ALIAS_USER_VERSION . '.' . TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID
-                )
-                ->leftJoin(
-                    //'vuv',
-                    TableConstants::TABLE_ALIAS_USER_VERSION,
-                    //'vds_user',
-                    $this->tableNameUser,
-                    //'vu',
-                    TableConstants::TABLE_ALIAS_USER,
-                    //'vuv.user_id = vu.id'
-                    TableConstants::TABLE_ALIAS_USER_VERSION . '.' .
-                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID . ' = ' . TableConstants::TABLE_ALIAS_USER .
-                    '.' . TableConstants::TABLE_USER_COLUMN_NAME_ID
-                )
-                ->leftJoin(
-                    //'vsv',
-                    TableConstants::TABLE_ALIAS_SP_VERSION,
-                    //'vds_sp_version',
-                    $this->tableNameSpVersion,
-                    //'vsv2',
-                    TableConstants::TABLE_ALIAS_SP_VERSION_2, // Another alias for self joining...
-                    //'vsv.id = vsv2.id AND vsv.id < vsv2.id' // To be able to get latest one...
-                    TableConstants::TABLE_ALIAS_SP_VERSION . '.' .
-                    TableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID . ' = ' . TableConstants::TABLE_ALIAS_SP_VERSION_2 .
-                    '.' . TableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID . ' AND ' .
-                    TableConstants::TABLE_ALIAS_SP_VERSION . '.' . TableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID .
-                    ' < ' . TableConstants::TABLE_ALIAS_SP_VERSION_2 . '.' .
-                    TableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID
-                )
-                ->leftJoin(
-                    //'vuv',
-                    TableConstants::TABLE_ALIAS_USER_VERSION,
-                    //'vds_user_version',
-                    $this->tableNameUserVersion,
-                    //'vuv2',
-                    TableConstants::TABLE_ALIAS_USER_VERSION_2, // Another alias for self joining...
-                    //'vuv.id = vuv2.id AND vuv.id < vuv2.id' // To be able to get latest one...
-                    TableConstants::TABLE_ALIAS_USER_VERSION . '.' .
-                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID . ' = ' .
-                    TableConstants::TABLE_ALIAS_USER_VERSION_2
-                    . '.' . TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID . ' AND ' .
-                    TableConstants::TABLE_ALIAS_USER_VERSION . '.' . TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID .
-                    ' < ' . TableConstants::TABLE_ALIAS_USER_VERSION_2 . '.' .
-                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID
-                )
-                ->where(
-                    //'vu.identifier_hash_sha256 = ' .
-                    TableConstants::TABLE_ALIAS_USER . '.' .
-                    TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 . ' = ' .
-                    $lastMetadataAndAttributesQueryBuilder->createNamedParameter($userIdentifierHashSha256)
-                )
-                ->andWhere(
-                    //'vsv2.id IS NULL'
-                    TableConstants::TABLE_ALIAS_SP_VERSION_2 . '.' . TableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID
-                    . ' IS NULL'
-                )
-                ->andWhere(
-                    //'vuv2.id IS NULL'
-                    TableConstants::TABLE_ALIAS_USER_VERSION_2 . '.' .
-                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID . ' IS NULL'
-                );
-
-            $numberOfAuthentications = $authenticationEventsQueryBuilder->executeQuery()->fetchAllAssociativeIndexed();
-            $lastMetadataAndAttributes =
-                $lastMetadataAndAttributesQueryBuilder->executeQuery()->fetchAllAssociativeIndexed();
-
-            return array_merge_recursive($numberOfAuthentications, $lastMetadataAndAttributes);
-        } catch (Throwable $exception) {
-            $message = sprintf(
-                'Error executing query to get connected organizations. Error was: %s.',
-                $exception->getMessage()
-            );
-            $this->logger->error($message, compact('userIdentifierHashSha256'));
-            throw new StoreException($message, (int)$exception->getCode(), $exception);
-        }
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public function getActivity(string $userIdentifierHashSha256, int $maxResults, int $firstResult): array
-    {
-        try {
-            $authenticationEventsQueryBuilder = $this->connection->dbal()->createQueryBuilder();
-
-            /** @psalm-suppress TooManyArguments */
-            $authenticationEventsQueryBuilder->select(
-                //'vae.happened_at',
-                TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' .
-                TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT,
-                TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' .
-                TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS,
-                TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' .
-                TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION,
-                //'vsv.metadata AS sp_metadata',
-                TableConstants::TABLE_ALIAS_SP_VERSION . '.' . TableConstants::TABLE_SP_VERSION_COLUMN_NAME_METADATA .
-                ' AS ' . TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA,
-                //'vuv.attributes AS user_attributes'
-                TableConstants::TABLE_ALIAS_USER_VERSION . '.' .
-                TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES . ' AS ' .
-                TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES
-            )->from(
-                //'vds_authentication_event', 'vae'
-                $this->tableNameAuthenticationEvent,
-                //'vae'
-                TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT
-            )
-                ->leftJoin(
-                    //'vae',
-                    TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT,
-                    //'vds_idp_sp_user_version',
-                    $this->tableNameIdpSpUserVersion,
-                    //'visuv',
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
-                    //'vae.idp_sp_user_version_id = visuv.id'
-                    TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' .
-                    TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_IDP_SP_USER_VERSION_ID . ' = ' .
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
-                    TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID
-                )
-                ->leftJoin(
-                    //'visuv',
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
-                    //'vds_sp_version',
-                    $this->tableNameSpVersion,
-                    //'vsv',
-                    TableConstants::TABLE_ALIAS_SP_VERSION,
-                    //'visuv.sp_version_id = vsv.id'
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
-                    TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID . ' = ' .
-                    TableConstants::TABLE_ALIAS_SP_VERSION . '.' . TableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID
-                )
-                ->leftJoin(
-                    //'vsv',
-                    TableConstants::TABLE_ALIAS_SP_VERSION,
-                    //'vds_sp',
-                    $this->tableNameSp,
-                    //'vs',
-                    TableConstants::TABLE_ALIAS_SP,
-                    //'vsv.sp_id = vs.id'
-                    TableConstants::TABLE_ALIAS_SP_VERSION . '.' . TableConstants::TABLE_SP_VERSION_COLUMN_NAME_SP_ID .
-                    ' = ' . TableConstants::TABLE_ALIAS_SP . '.' . TableConstants::TABLE_SP_COLUMN_NAME_ID
-                )
-                ->leftJoin(
-                    //'visuv',
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
-                    //'vds_user_version',
-                    $this->tableNameUserVersion,
-                    //'vuv',
-                    TableConstants::TABLE_ALIAS_USER_VERSION,
-                    //'visuv.user_version_id = vuv.id'
-                    TableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
-                    TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID . ' = ' .
-                    TableConstants::TABLE_ALIAS_USER_VERSION . '.' . TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID
-                )
-                ->leftJoin(
-                    //'vuv',
-                    TableConstants::TABLE_ALIAS_USER_VERSION,
-                    //'vds_user',
-                    $this->tableNameUser,
-                    //'vu',
-                    TableConstants::TABLE_ALIAS_USER,
-                    //'vuv.user_id = vu.id'
-                    TableConstants::TABLE_ALIAS_USER_VERSION . '.' .
-                    TableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID . ' = ' . TableConstants::TABLE_ALIAS_USER .
-                    '.' . TableConstants::TABLE_USER_COLUMN_NAME_ID
-                )
-                ->where(
-                    //'vu.identifier_hash_sha256 = ' .
-                    TableConstants::TABLE_ALIAS_USER . '.' .
-                    TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 . ' = ' .
-                    $authenticationEventsQueryBuilder->createNamedParameter($userIdentifierHashSha256)
-                )
-                ->orderBy(
-                //'vae.id',
-                    TableConstants::TABLE_ALIAS_AUTHENTICATION_EVENT . '.' .
-                    TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_ID,
-                    'DESC'
-                )
-            ->setMaxResults($maxResults)
-            ->setFirstResult($firstResult);
-
-            return $authenticationEventsQueryBuilder->executeQuery()->fetchAllAssociative();
-        } catch (Throwable $exception) {
-            $message = sprintf(
-                'Error executing query to get connected organizations. Error was: %s.',
-                $exception->getMessage()
-            );
-            $this->logger->error($message, compact('userIdentifierHashSha256'));
-            throw new StoreException($message, (int)$exception->getCode(), $exception);
-        }
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public function deleteAuthenticationEventsOlderThan(DateTimeImmutable $dateTime): void
-    {
-        try {
-            $queryBuilder = $this->connection->dbal()->createQueryBuilder();
-
-            $queryBuilder->delete($this->tableNameAuthenticationEvent)
-                ->where(
-                    $queryBuilder->expr()->lt(
-                        TableConstants::TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT,
-                        $queryBuilder->createNamedParameter($dateTime, Types::DATETIME_IMMUTABLE)
-                    )
-                )->executeStatement();
-        } catch (Throwable $exception) {
-            $message = sprintf(
-                'Error executing query to delete old authentication events. Error was: %s.',
-                $exception->getMessage()
-            );
-            $this->logger->error($message);
-            throw new StoreException($message, (int)$exception->getCode(), $exception);
-        }
-    }
 }
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/TableConstants.php b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/TableConstants.php
similarity index 61%
rename from src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/TableConstants.php
rename to src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/TableConstants.php
index df6eb7a..72da460 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/TableConstants.php
+++ b/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/TableConstants.php
@@ -2,20 +2,14 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store;
 
-class TableConstants
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\TableConstants as BaseTableConstants;
+
+class TableConstants extends BaseTableConstants
 {
     public const TABLE_PREFIX = 'vds_'; // versioned data store
 
-    // Any SAML entity ID should have maximum 1024 chars per
-    // https://stackoverflow.com/questions/24196369/what-to-present-at-saml-entityid-url
-    public const COLUMN_ENTITY_ID_LENGTH = 1024;
-    public const COLUMN_HASH_SHA265_HEXITS_LENGTH = 64;
-    public const COLUMN_IP_ADDRESS_LENGTH = 45;
-    public const COLUMN_AUTHENTICATION_PROTOCOL_DESIGNATION_LENGTH = 16;
-
-
     // Table 'idp'
     public const TABLE_NAME_IDP = 'idp';
     public const TABLE_ALIAS_IDP = self::TABLE_PREFIX . 'i';
@@ -69,7 +63,7 @@ class TableConstants
     public const TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256 = 'attributes_hash_sha256';
     public const TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT = 'created_at';
 
-    // Attribute versions released to SP version
+    // Attribute versions released to SP version by IdP version
     public const TABLE_NAME_IDP_SP_USER_VERSION = 'idp_sp_user_version';
     public const TABLE_ALIAS_IDP_SP_USER_VERSION = self::TABLE_PREFIX . 'isuv';
     public const TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID = 'id';
@@ -77,31 +71,4 @@ class TableConstants
     public const TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID = 'sp_version_id';
     public const TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID = 'user_version_id';
     public const TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_CREATED_AT = 'created_at';
-
-    // Table 'authentication_event'.
-    public const TABLE_NAME_AUTHENTICATION_EVENT = 'authentication_event';
-    public const TABLE_ALIAS_AUTHENTICATION_EVENT = self::TABLE_PREFIX . 'ae';
-    public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_ID = 'id';
-    public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_IDP_SP_USER_VERSION_ID = 'idp_sp_user_version_id';
-    public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_HAPPENED_AT = 'happened_at';
-    public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CLIENT_IP_ADDRESS = 'client_ip_address';
-    public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION =
-        'authentication_protocol_designation';
-    public const TABLE_AUTHENTICATION_EVENT_COLUMN_NAME_CREATED_AT = 'created_at';
-
-    // Entity 'ConnectedOrganization' (service provider) related.
-    public const ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_SP_ENTITY_ID = 'sp_entity_id';
-    public const ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS = 'number_of_authentications';
-    public const ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_LAST_AUTHENTICATION_AT = 'last_authentication_at';
-    public const ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_FIRST_AUTHENTICATION_AT = 'first_authentication_at';
-    public const ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_SP_METADATA = 'sp_metadata';
-    public const ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_USER_ATTRIBUTES = 'user_attributes';
-
-    // Entity 'Activity' related.
-    public const ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA = 'sp_metadata';
-    public const ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES = 'user_attributes';
-    public const ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT = 'happened_at';
-    public const ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS = 'client_ip_address';
-    public const ENTITY_ACTIVITY_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION =
-        'authentication_protocol_designation';
 }
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/HashDecoratedState.php b/src/Data/Stores/Accounting/Bases/HashDecoratedState.php
similarity index 96%
rename from src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/HashDecoratedState.php
rename to src/Data/Stores/Accounting/Bases/HashDecoratedState.php
index 07c60cf..39aced9 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/HashDecoratedState.php
+++ b/src/Data/Stores/Accounting/Bases/HashDecoratedState.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases;
 
 use SimpleSAML\Module\accounting\Entities\Interfaces\StateInterface;
 use SimpleSAML\Module\accounting\Services\HelpersManager;
diff --git a/src/Data/Stores/Accounting/Bases/TableConstants.php b/src/Data/Stores/Accounting/Bases/TableConstants.php
new file mode 100644
index 0000000..5f5d656
--- /dev/null
+++ b/src/Data/Stores/Accounting/Bases/TableConstants.php
@@ -0,0 +1,15 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases;
+
+class TableConstants
+{
+    // Any SAML entity ID should have maximum 1024 chars per
+    // https://stackoverflow.com/questions/24196369/what-to-present-at-saml-entityid-url
+    public const COLUMN_ENTITY_ID_LENGTH = 1024;
+    public const COLUMN_HASH_SHA265_HEXITS_LENGTH = 64;
+    public const COLUMN_IP_ADDRESS_LENGTH = 45;
+    public const COLUMN_AUTHENTICATION_PROTOCOL_DESIGNATION_LENGTH = 16;
+}
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store.php
new file mode 100644
index 0000000..8e84d9c
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store.php
@@ -0,0 +1,154 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current;
+
+use DateTimeImmutable;
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store as BaseStore;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Repository;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\RawConnectedService;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory;
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\ConnectedServicesInterface;
+use SimpleSAML\Module\accounting\Entities\Authentication\Event;
+use SimpleSAML\Module\accounting\Entities\ConnectedService;
+use SimpleSAML\Module\accounting\Entities\User;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
+use Throwable;
+
+class Store extends BaseStore implements ConnectedServicesInterface
+{
+    protected Repository $repository;
+
+    /**
+     * @throws StoreException
+     */
+    public function __construct(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionKey = null,
+        string $connectionType = ModuleConfiguration\ConnectionType::MASTER,
+        Factory $connectionFactory = null,
+        HelpersManager $helpersManager = null,
+        Repository $repository = null
+    ) {
+        parent::__construct(
+            $moduleConfiguration,
+            $logger,
+            $connectionKey,
+            $connectionType,
+            $connectionFactory,
+            $helpersManager
+        );
+
+        $this->repository = $repository ?? new Repository($this->connection, $this->logger);
+    }
+
+    /**
+     * Build store instance.
+     * @throws StoreException
+     */
+    public static function build(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionKey = null,
+        string $connectionType = ModuleConfiguration\ConnectionType::MASTER
+    ): self {
+        return new self(
+            $moduleConfiguration,
+            $logger,
+            $connectionKey,
+            $connectionType
+        );
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function persist(Event $authenticationEvent): void
+    {
+        $hashDecoratedState = new HashDecoratedState($authenticationEvent->getState());
+
+        $spId = $this->resolveSpId($hashDecoratedState);
+        $userId = $this->resolveUserId($hashDecoratedState);
+        $userVersionId = $this->resolveUserVersionId($userId, $hashDecoratedState);
+
+        /** @psalm-suppress MixedAssignment */
+        $connectedServiceId = $this->repository->getConnectedService($spId, $userId)->fetchOne();
+
+        if ($connectedServiceId !== false) {
+            $this->repository->updateConnectedServiceVersionCount(
+                (int)$connectedServiceId,
+                $userVersionId,
+                $authenticationEvent->getHappenedAt()
+            );
+        } else {
+            $this->repository->insertConnectedService(
+                $spId,
+                $userId,
+                $userVersionId,
+                $authenticationEvent->getHappenedAt()
+            );
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function getConnectedServices(string $userIdentifier): ConnectedService\Bag
+    {
+        $connectedServiceProviderBag = new ConnectedService\Bag();
+
+        $userIdentifierHashSha256 = $this->helpersManager->getHash()->getSha256($userIdentifier);
+
+        $results = $this->repository->getConnectedServices($userIdentifierHashSha256);
+
+        if (empty($results)) {
+            return $connectedServiceProviderBag;
+        }
+
+        try {
+            $databasePlatform = $this->connection->dbal()->getDatabasePlatform();
+
+            /** @var array $result */
+            foreach ($results as $result) {
+                $rawConnectedServiceProvider = new RawConnectedService($result, $databasePlatform);
+
+                $serviceProvider = $this->helpersManager
+                    ->getProviderResolver()
+                    ->forServiceFromMetadataArray($rawConnectedServiceProvider->getServiceProviderMetadata());
+                $user = new User($rawConnectedServiceProvider->getUserAttributes());
+
+                $connectedServiceProviderBag->addOrReplace(
+                    new ConnectedService(
+                        $serviceProvider,
+                        $rawConnectedServiceProvider->getNumberOfAuthentications(),
+                        $rawConnectedServiceProvider->getLastAuthenticationAt(),
+                        $rawConnectedServiceProvider->getFirstAuthenticationAt(),
+                        $user
+                    )
+                );
+            }
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error populating connected service provider bag. Error was: %s',
+                $exception->getMessage()
+            );
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+
+        return $connectedServiceProviderBag;
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function deleteDataOlderThan(DateTimeImmutable $dateTime): void
+    {
+        $this->repository->deleteConnectedServicesOlderThan($dateTime);
+    }
+}
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505100CreateSpTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505100CreateSpTable.php
new file mode 100644
index 0000000..8f5d2ee
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505100CreateSpTable.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateSpTable;
+
+class Version20240505100CreateSpTable extends CreateSpTable
+{
+}
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505200CreateUserTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505200CreateUserTable.php
new file mode 100644
index 0000000..2120b20
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505200CreateUserTable.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations\CreateUserTable;
+
+class Version20240505200CreateUserTable extends CreateUserTable
+{
+}
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505300CreateUserVersionTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505300CreateUserVersionTable.php
new file mode 100644
index 0000000..f3a60f2
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505300CreateUserVersionTable.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Migrations;
+
+class Version20240505300CreateUserVersionTable extends Migrations\CreateUserVersionTable
+{
+}
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505400CreateConnectedServiceTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505400CreateConnectedServiceTable.php
new file mode 100644
index 0000000..bd61e6d
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Migrations/Version20240505400CreateConnectedServiceTable.php
@@ -0,0 +1,104 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store\Migrations;
+
+use Doctrine\DBAL\Schema\Table;
+use Doctrine\DBAL\Types\Types;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
+use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
+use Throwable;
+
+use function sprintf;
+
+class Version20240505400CreateConnectedServiceTable extends AbstractMigration
+{
+    protected function getLocalTablePrefix(): string
+    {
+        return 'cds_';
+    }
+
+    /**
+     * @inheritDoc
+     * @throws MigrationException
+     */
+    public function run(): void
+    {
+        $tableName = $this->preparePrefixedTableName('connected_service');
+
+        try {
+            $table = new Table($tableName);
+
+            $table->addColumn('id', Types::BIGINT)
+                ->setUnsigned(true)
+                ->setAutoincrement(true);
+
+            $table->addColumn('sp_id', Types::BIGINT)
+                ->setUnsigned(true);
+
+            $table->addColumn('user_id', Types::BIGINT)
+                ->setUnsigned(true);
+
+            $table->addColumn('user_version_id', Types::BIGINT)
+                ->setUnsigned(true);
+
+            $table->addColumn('first_authentication_at', Types::DATETIMETZ_IMMUTABLE);
+
+            $table->addColumn('last_authentication_at', Types::DATETIMETZ_IMMUTABLE);
+
+            $table->addColumn('count', Types::BIGINT)->setUnsigned(true);
+
+            $table->addColumn('created_at', Types::DATETIMETZ_IMMUTABLE);
+
+            $table->addColumn('updated_at', Types::DATETIMETZ_IMMUTABLE);
+
+            $table->setPrimaryKey(['id']);
+
+            $table->addForeignKeyConstraint(
+                $this->preparePrefixedTableName('sp'),
+                ['sp_id'],
+                ['id']
+            );
+
+            $table->addForeignKeyConstraint(
+                $this->preparePrefixedTableName('user'),
+                ['user_id'],
+                ['id']
+            );
+
+            $table->addForeignKeyConstraint(
+                $this->preparePrefixedTableName('user_version'),
+                ['user_version_id'],
+                ['id']
+            );
+
+            // Old data can be deleted using last_authentication_at column, so add index for it.
+            $table->addIndex(['last_authentication_at']);
+
+            $table->addUniqueConstraint(['sp_id', 'user_id']);
+
+            $this->schemaManager->createTable($table);
+        } catch (Throwable $exception) {
+            throw $this->prepareGenericMigrationException(
+                sprintf('Error creating table \'%s.', $tableName),
+                $exception
+            );
+        }
+    }
+
+    /**
+     * @inheritDoc
+     * @throws MigrationException
+     */
+    public function revert(): void
+    {
+        $tableName = $this->preparePrefixedTableName('connected_service');
+
+        try {
+            $this->schemaManager->dropTable($tableName);
+        } catch (Throwable $exception) {
+            throw $this->prepareGenericMigrationException(sprintf('Could not drop table %s.', $tableName), $exception);
+        }
+    }
+}
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Repository.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Repository.php
new file mode 100644
index 0000000..74a8e62
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/Repository.php
@@ -0,0 +1,332 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store;
+
+use DateTimeImmutable;
+use Doctrine\DBAL\ParameterType;
+use Doctrine\DBAL\Result;
+use Doctrine\DBAL\Types\Types;
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\Repository as BaseRepository;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants
+    as BaseTableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use Throwable;
+
+class Repository extends BaseRepository
+{
+    protected string $tableNameConnectedService;
+
+    public function __construct(Connection $connection, LoggerInterface $logger)
+    {
+        parent::__construct($connection, $logger);
+
+        $this->tableNameConnectedService = $this->preparePrefixedTableName(
+            TableConstants::TABLE_NAME_CONNECTED_SERVICE
+        );
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function getConnectedService(int $spId, int $userId): Result
+    {
+        try {
+            $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+            /** @psalm-suppress TooManyArguments */
+            $queryBuilder->select(
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_ID,
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_SP_ID,
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID,
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID,
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT,
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT,
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT,
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT,
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT,
+            )
+            ->from($this->tableNameConnectedService)
+            ->where(
+                $queryBuilder->expr()->and(
+                    $queryBuilder->expr()->eq(
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_SP_ID,
+                        $queryBuilder->createNamedParameter($spId, ParameterType::INTEGER)
+                    ),
+                    $queryBuilder->expr()->eq(
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID,
+                        $queryBuilder->createNamedParameter($userId, ParameterType::INTEGER)
+                    )
+                )
+            )->setMaxResults(1);
+
+            return $queryBuilder->executeQuery();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing query to get connected service. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->error($message, compact('spId', 'userId'));
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function insertConnectedService(
+        int $spId,
+        int $userId,
+        int $userVersionId,
+        DateTimeImmutable $firstAuthenticationAt = null,
+        DateTimeImmutable $lastAuthenticationAt = null,
+        int $count = 1,
+        DateTimeImmutable $createdUpdatedAt = null
+    ): void {
+        $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+        $firstAuthenticationAt = $firstAuthenticationAt ?? new DateTimeImmutable();
+        $lastAuthenticationAt = $lastAuthenticationAt ?? $firstAuthenticationAt;
+        $count = max($count, 1);
+        $createdUpdatedAt = $createdUpdatedAt ?? new DateTimeImmutable();
+
+        $queryBuilder->insert($this->tableNameConnectedService)
+            ->values(
+                [
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_SP_ID => ':' .
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_SP_ID,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID => ':' .
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID => ':' .
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT => ':' .
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT => ':' .
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => ':' .
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => ':' .
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => ':' .
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT,
+                ]
+            )
+            ->setParameters(
+                [
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_SP_ID => $spId,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID => $userId,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID => $userVersionId,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT =>
+                        $firstAuthenticationAt,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT =>
+                        $lastAuthenticationAt,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => $count,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => $createdUpdatedAt,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => $createdUpdatedAt,
+                ],
+                [
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_SP_ID => Types::BIGINT,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID => Types::BIGINT,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID => Types::BIGINT,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT =>
+                        Types::DATETIMETZ_IMMUTABLE,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT =>
+                        Types::DATETIMETZ_IMMUTABLE,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => Types::BIGINT,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => Types::DATETIMETZ_IMMUTABLE,
+                ]
+            );
+
+        try {
+            $queryBuilder->executeStatement();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing query to insert connected service. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->error(
+                $message,
+                compact('spId', 'userId', 'count', 'firstAuthenticationAt', 'lastAuthenticationAt')
+            );
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function updateConnectedServiceVersionCount(
+        int $connectedServiceId,
+        int $userVersionId,
+        DateTimeImmutable $happenedAt,
+        int $incrementCountBy = 1
+    ): void {
+        $incrementCountBy = max($incrementCountBy, 1);
+
+        $updateCountQueryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+        $updateCountQueryBuilder->update($this->tableNameConnectedService)
+            ->set(
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT,
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT . ' + ' . $incrementCountBy
+            )
+            ->set(
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID,
+                ':' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID
+            )
+            ->set(
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT,
+                ':' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT
+            )
+            ->set(
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT,
+                ':' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT
+            )
+            ->setParameter(
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID,
+                $userVersionId,
+                Types::INTEGER
+            )
+            ->setParameter(
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT,
+                $happenedAt,
+                Types::DATETIMETZ_IMMUTABLE
+            )
+            ->setParameter(
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT,
+                new DateTimeImmutable(),
+                Types::DATETIMETZ_IMMUTABLE
+            )
+            ->where(
+                $updateCountQueryBuilder->expr()->and(
+                    $updateCountQueryBuilder->expr()->eq(
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_ID,
+                        $updateCountQueryBuilder->createNamedParameter($connectedServiceId, Types::INTEGER)
+                    )
+                )
+            );
+
+        try {
+            $updateCountQueryBuilder->executeStatement();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing update count for connected service. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->error(
+                $message,
+                compact('connectedServiceId', 'userVersionId', 'happenedAt', 'incrementCountBy')
+            );
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function getConnectedServices(string $userIdentifierHashSha256): array
+    {
+        try {
+            $connectedServicesQueryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+            /** @psalm-suppress TooManyArguments */
+            $connectedServicesQueryBuilder->select(
+                BaseTableConstants::TABLE_ALIAS_SP . '.' .
+                BaseTableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID . ' AS ' .
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_ENTITY_ID,
+                TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' .
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT . ' AS ' .
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS,
+                TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' .
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT . ' AS ' .
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT,
+                TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' .
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT . ' AS ' .
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT,
+                BaseTableConstants::TABLE_ALIAS_SP . '.' .
+                BaseTableConstants::TABLE_SP_COLUMN_NAME_METADATA . ' AS ' .
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA,
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES . ' AS ' .
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES,
+            )->from($this->tableNameConnectedService, TableConstants::TABLE_ALIAS_CONNECTED_SERVICE)
+            ->innerJoin(
+                //'ccs',
+                TableConstants::TABLE_ALIAS_CONNECTED_SERVICE,
+                //'cds_sp',
+                $this->tableNameSp,
+                //'cs',
+                BaseTableConstants::TABLE_ALIAS_SP,
+                //'ccs.sp_id = cs.id'
+                TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' .
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_SP_ID .
+                ' = ' . BaseTableConstants::TABLE_ALIAS_SP . '.' .
+                BaseTableConstants::TABLE_SP_COLUMN_NAME_ID
+            )
+            ->innerJoin(
+                TableConstants::TABLE_ALIAS_CONNECTED_SERVICE,
+                $this->tableNameUser,
+                BaseTableConstants::TABLE_ALIAS_USER,
+                TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' .
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID . ' = ' .
+                BaseTableConstants::TABLE_ALIAS_USER . '.' .
+                BaseTableConstants::TABLE_USER_COLUMN_NAME_ID
+            )
+            ->innerJoin(
+                TableConstants::TABLE_ALIAS_CONNECTED_SERVICE,
+                $this->tableNameUserVersion,
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION,
+                TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' .
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID . ' = ' .
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID
+            )
+            ->where(
+                BaseTableConstants::TABLE_ALIAS_USER . '.' .
+                BaseTableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 . ' = ' .
+                $connectedServicesQueryBuilder->createNamedParameter($userIdentifierHashSha256)
+            )->orderBy(
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS,
+                'DESC'
+            );
+
+            return $connectedServicesQueryBuilder->executeQuery()->fetchAllAssociative();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing query to get connected services. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->error($message, compact('userIdentifierHashSha256'));
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function deleteConnectedServicesOlderThan(DateTimeImmutable $dateTime): void
+    {
+        try {
+            $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+            $queryBuilder->delete($this->tableNameConnectedService)
+                ->where(
+                    $queryBuilder->expr()->lt(
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT,
+                        $queryBuilder->createNamedParameter($dateTime, Types::DATETIME_IMMUTABLE)
+                    )
+                )->executeStatement();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing query to delete old connected services. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->error($message);
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+}
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/TableConstants.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/TableConstants.php
new file mode 100644
index 0000000..d0331be
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Current/Store/TableConstants.php
@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Current\Store;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Current\Store\TableConstants
+    as BaseTableConstants;
+
+class TableConstants
+{
+    // Table 'connected_service' (connected organizations).
+    public const TABLE_NAME_CONNECTED_SERVICE = 'connected_service';
+    public const TABLE_ALIAS_CONNECTED_SERVICE = BaseTableConstants::TABLE_PREFIX . 'cs';
+    public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_ID = 'id'; // int
+    public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_ID = 'idp_id'; // int
+    public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_SP_ID = 'sp_id'; // int
+    public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_ID = 'user_id'; // int
+    public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_USER_VERSION_ID = 'user_version_id'; // int
+    public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT = 'first_authentication_at'; // datetime
+    public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT = 'last_authentication_at'; // datetime
+    public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT = 'count'; // int
+    public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT = 'created_at'; // datetime
+    public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT = 'updated_at'; // datetime
+
+    // Entity 'ConnectedService' (service provider) related.
+    public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_ENTITY_ID = 'sp_entity_id';
+    public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS = 'number_of_authentications';
+    public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT = 'last_authentication_at';
+    public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT = 'first_authentication_at';
+    public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA = 'sp_metadata';
+    public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES = 'user_attributes';
+}
diff --git a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawConnectedServiceProvider.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/RawConnectedService.php
similarity index 68%
rename from src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawConnectedServiceProvider.php
rename to src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/RawConnectedService.php
index d730b31..41334ea 100644
--- a/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawConnectedServiceProvider.php
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/RawConnectedService.php
@@ -2,14 +2,15 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal;
 
 use DateTimeImmutable;
 use Doctrine\DBAL\Platforms\AbstractPlatform;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity;
 use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
-use SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractRawEntity;
 
-class RawConnectedServiceProvider extends AbstractRawEntity
+class RawConnectedService extends AbstractRawEntity
 {
     protected int $numberOfAuthentications;
     protected DateTimeImmutable $lastAuthenticationAt;
@@ -22,23 +23,23 @@ class RawConnectedServiceProvider extends AbstractRawEntity
         parent::__construct($rawRow, $abstractPlatform);
 
         $this->numberOfAuthentications = (int)$rawRow[
-            TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS
+            TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS
         ];
 
         $this->lastAuthenticationAt = $this->resolveDateTimeImmutable(
-            $rawRow[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_LAST_AUTHENTICATION_AT]
+            $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT]
         );
 
         $this->firstAuthenticationAt = $this->resolveDateTimeImmutable(
-            $rawRow[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_FIRST_AUTHENTICATION_AT]
+            $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT]
         );
 
         $this->serviceProviderMetadata = $this->resolveServiceProviderMetadata(
-            (string)$rawRow[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_SP_METADATA]
+            (string)$rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA]
         );
 
         $this->userAttributes = $this->resolveUserAttributes(
-            (string)$rawRow[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_USER_ATTRIBUTES]
+            (string)$rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES]
         );
     }
 
@@ -88,11 +89,11 @@ class RawConnectedServiceProvider extends AbstractRawEntity
     protected function validate(array $rawRow): void
     {
         $columnsToCheck = [
-            TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS,
-            TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_LAST_AUTHENTICATION_AT,
-            TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_FIRST_AUTHENTICATION_AT,
-            TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_SP_METADATA,
-            TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_USER_ATTRIBUTES,
+            TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS,
+            TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT,
+            TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT,
+            TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA,
+            TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES,
         ];
 
         foreach ($columnsToCheck as $column) {
@@ -102,44 +103,44 @@ class RawConnectedServiceProvider extends AbstractRawEntity
         }
 
         if (
-            ! is_numeric($rawRow[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS])
+            ! is_numeric($rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS])
         ) {
             $message = sprintf(
                 'Column %s must be numeric.',
-                TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS
             );
             throw new UnexpectedValueException($message);
         }
 
         /** @noinspection DuplicatedCode */
-        if (! is_string($rawRow[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_LAST_AUTHENTICATION_AT])) {
+        if (! is_string($rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT])) {
             $message = sprintf(
                 'Column %s must be string.',
-                TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_LAST_AUTHENTICATION_AT
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT
             );
             throw new UnexpectedValueException($message);
         }
 
-        if (! is_string($rawRow[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_FIRST_AUTHENTICATION_AT])) {
+        if (! is_string($rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT])) {
             $message = sprintf(
                 'Column %s must be string.',
-                TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_FIRST_AUTHENTICATION_AT
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT
             );
             throw new UnexpectedValueException($message);
         }
 
-        if (! is_string($rawRow[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_SP_METADATA])) {
+        if (! is_string($rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA])) {
             $message = sprintf(
                 'Column %s must be string.',
-                TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_SP_METADATA
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA
             );
             throw new UnexpectedValueException($message);
         }
 
-        if (! is_string($rawRow[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_USER_ATTRIBUTES])) {
+        if (! is_string($rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES])) {
             $message = sprintf(
                 'Column %s must be string.',
-                TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_USER_ATTRIBUTES
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES
             );
             throw new UnexpectedValueException($message);
         }
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store.php
new file mode 100644
index 0000000..6887a5b
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store.php
@@ -0,0 +1,154 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned;
+
+use DateTimeImmutable;
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store as BaseStore;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\RawConnectedService;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Repository;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory;
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\ConnectedServicesInterface;
+use SimpleSAML\Module\accounting\Entities\Authentication\Event;
+use SimpleSAML\Module\accounting\Entities\ConnectedService;
+use SimpleSAML\Module\accounting\Entities\User;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
+use Throwable;
+
+class Store extends BaseStore implements ConnectedServicesInterface
+{
+    protected Repository $repository;
+
+    /**
+     * @throws StoreException
+     */
+    public function __construct(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionKey = null,
+        string $connectionType = ModuleConfiguration\ConnectionType::MASTER,
+        Factory $connectionFactory = null,
+        HelpersManager $helpersManager = null,
+        Repository $repository = null
+    ) {
+        parent::__construct(
+            $moduleConfiguration,
+            $logger,
+            $connectionKey,
+            $connectionType,
+            $connectionFactory,
+            $helpersManager
+        );
+
+        $this->repository = $repository ?? new Repository($this->connection, $this->logger);
+    }
+
+    /**
+     * Build store instance.
+     * @throws StoreException
+     */
+    public static function build(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionKey = null,
+        string $connectionType = ModuleConfiguration\ConnectionType::MASTER
+    ): self {
+        return new self(
+            $moduleConfiguration,
+            $logger,
+            $connectionKey,
+            $connectionType
+        );
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function persist(Event $authenticationEvent): void
+    {
+        $hashDecoratedState = new HashDecoratedState($authenticationEvent->getState());
+
+        $idpId = $this->resolveIdpId($hashDecoratedState);
+        $idpVersionId = $this->resolveIdpVersionId($idpId, $hashDecoratedState);
+        $spId = $this->resolveSpId($hashDecoratedState);
+        $spVersionId = $this->resolveSpVersionId($spId, $hashDecoratedState);
+        $userId = $this->resolveUserId($hashDecoratedState);
+        $userVersionId = $this->resolveUserVersionId($userId, $hashDecoratedState);
+        $idpSpUserVersionId = $this->resolveIdpSpUserVersionId($idpVersionId, $spVersionId, $userVersionId);
+
+        /** @psalm-suppress MixedAssignment */
+        if (
+            ($connectedServiceId = $this->repository->getConnectedService($idpSpUserVersionId)->fetchOne()) !== false
+        ) {
+            $this->repository->updateConnectedServiceVersionCount(
+                (int)$connectedServiceId,
+                $authenticationEvent->getHappenedAt()
+            );
+        } else {
+            $this->repository->insertConnectedService($idpSpUserVersionId, $authenticationEvent->getHappenedAt());
+        }
+
+        $this->repository->touchConnectedServiceVersionsTimestamp($userId, $spId);
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function getConnectedServices(string $userIdentifier): ConnectedService\Bag
+    {
+        $connectedServiceProviderBag = new ConnectedService\Bag();
+
+        $userIdentifierHashSha256 = $this->helpersManager->getHash()->getSha256($userIdentifier);
+
+        $results = $this->repository->getConnectedServices($userIdentifierHashSha256);
+
+        if (empty($results)) {
+            return $connectedServiceProviderBag;
+        }
+
+        try {
+            $databasePlatform = $this->connection->dbal()->getDatabasePlatform();
+
+            /** @var array $result */
+            foreach ($results as $result) {
+                $rawConnectedServiceProvider = new RawConnectedService($result, $databasePlatform);
+
+                $serviceProvider = $this->helpersManager
+                    ->getProviderResolver()
+                    ->forServiceFromMetadataArray($rawConnectedServiceProvider->getServiceProviderMetadata());
+                $user = new User($rawConnectedServiceProvider->getUserAttributes());
+
+                $connectedServiceProviderBag->addOrReplace(
+                    new ConnectedService(
+                        $serviceProvider,
+                        $rawConnectedServiceProvider->getNumberOfAuthentications(),
+                        $rawConnectedServiceProvider->getLastAuthenticationAt(),
+                        $rawConnectedServiceProvider->getFirstAuthenticationAt(),
+                        $user
+                    )
+                );
+            }
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error populating connected service provider bag. Error was: %s',
+                $exception->getMessage()
+            );
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+
+        return $connectedServiceProviderBag;
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function deleteDataOlderThan(DateTimeImmutable $dateTime): void
+    {
+        $this->repository->deleteConnectedServicesOlderThan($dateTime);
+    }
+}
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTable.php
new file mode 100644
index 0000000..69e5d76
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTable.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
+
+class Version20220801000000CreateIdpTable extends Migrations\CreateIdpTable
+{
+}
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTable.php
new file mode 100644
index 0000000..70d4af5
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTable.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
+
+class Version20220801000100CreateIdpVersionTable extends Migrations\CreateIdpVersionTable
+{
+}
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTable.php
new file mode 100644
index 0000000..7a23c8e
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTable.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
+
+class Version20220801000200CreateSpTable extends Migrations\CreateSpTable
+{
+}
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTable.php
new file mode 100644
index 0000000..4a30007
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTable.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
+
+class Version20220801000300CreateSpVersionTable extends Migrations\CreateSpVersionTable
+{
+}
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTable.php
new file mode 100644
index 0000000..30b6b9d
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTable.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
+
+class Version20220801000400CreateUserTable extends Migrations\CreateUserTable
+{
+}
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTable.php
new file mode 100644
index 0000000..71c0190
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTable.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
+
+class Version20220801000500CreateUserVersionTable extends Migrations\CreateUserVersionTable
+{
+}
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTable.php
new file mode 100644
index 0000000..6d4427b
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTable.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
+
+class Version20220801000600CreateIdpSpUserVersionTable extends Migrations\CreateIdpSpUserVersionTable
+{
+}
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateConnectedServiceTable.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateConnectedServiceTable.php
new file mode 100644
index 0000000..10df6dc
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateConnectedServiceTable.php
@@ -0,0 +1,86 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations;
+
+use Doctrine\DBAL\Schema\Table;
+use Doctrine\DBAL\Types\Types;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
+use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
+use Throwable;
+
+use function sprintf;
+
+class Version20220801000700CreateConnectedServiceTable extends AbstractMigration
+{
+    protected function getLocalTablePrefix(): string
+    {
+        return 'vds_';
+    }
+
+    /**
+     * @inheritDoc
+     * @throws MigrationException
+     */
+    public function run(): void
+    {
+        $tableName = $this->preparePrefixedTableName('connected_service');
+
+        try {
+            $table = new Table($tableName);
+
+            $table->addColumn('id', Types::BIGINT)
+                ->setUnsigned(true)
+                ->setAutoincrement(true);
+
+            $table->addColumn('idp_sp_user_version_id', Types::BIGINT)
+                ->setUnsigned(true);
+
+            $table->addColumn('first_authentication_at', Types::DATETIMETZ_IMMUTABLE);
+
+            $table->addColumn('last_authentication_at', Types::DATETIMETZ_IMMUTABLE);
+
+            $table->addColumn('count', Types::BIGINT)->setUnsigned(true);
+
+            $table->addColumn('created_at', Types::DATETIMETZ_IMMUTABLE);
+
+            $table->addColumn('updated_at', Types::DATETIMETZ_IMMUTABLE);
+
+            $table->setPrimaryKey(['id']);
+
+            $table->addForeignKeyConstraint(
+                $this->preparePrefixedTableName('idp_sp_user_version'),
+                ['idp_sp_user_version_id'],
+                ['id']
+            );
+
+            // Old data can be deleted using updated_at column, so add index for it.
+            $table->addIndex(['updated_at']);
+
+            $table->addUniqueConstraint(['idp_sp_user_version_id']);
+
+            $this->schemaManager->createTable($table);
+        } catch (Throwable $exception) {
+            throw $this->prepareGenericMigrationException(
+                sprintf('Error creating table \'%s.', $tableName),
+                $exception
+            );
+        }
+    }
+
+    /**
+     * @inheritDoc
+     * @throws MigrationException
+     */
+    public function revert(): void
+    {
+        $tableName = $this->preparePrefixedTableName('connected_service');
+
+        try {
+            $this->schemaManager->dropTable($tableName);
+        } catch (Throwable $exception) {
+            throw $this->prepareGenericMigrationException(sprintf('Could not drop table %s.', $tableName), $exception);
+        }
+    }
+}
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Repository.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Repository.php
new file mode 100644
index 0000000..51f2f5a
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/Repository.php
@@ -0,0 +1,612 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store;
+
+use DateTimeImmutable;
+use Doctrine\DBAL\ParameterType;
+use Doctrine\DBAL\Result;
+use Doctrine\DBAL\Types\Types;
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository as BaseRepository;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants
+    as BaseTableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use Throwable;
+
+class Repository extends BaseRepository
+{
+    protected string $tableNameConnectedService;
+
+    public function __construct(Connection $connection, LoggerInterface $logger)
+    {
+        parent::__construct($connection, $logger);
+
+        $this->tableNameConnectedService = $this->preparePrefixedTableName(
+            TableConstants::TABLE_NAME_CONNECTED_SERVICE
+        );
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function getConnectedService(int $idpSpUserVersionId): Result
+    {
+        try {
+            $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+            /** @psalm-suppress TooManyArguments */
+            $queryBuilder->select(
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_ID,
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID,
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT,
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT,
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT,
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT,
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT,
+            )
+                ->from($this->tableNameConnectedService)
+                ->where(
+                    $queryBuilder->expr()->and(
+                        $queryBuilder->expr()->eq(
+                            TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID,
+                            $queryBuilder->createNamedParameter($idpSpUserVersionId, ParameterType::INTEGER)
+                        )
+                    )
+                )->setMaxResults(1);
+
+            return $queryBuilder->executeQuery();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing query to get connected service. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->error($message, compact('idpSpUserVersionId'));
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function insertConnectedService(
+        int $idpSpUserVersionId,
+        DateTimeImmutable $firstAuthenticationAt = null,
+        DateTimeImmutable $lastAuthenticationAt = null,
+        int $count = 1,
+        DateTimeImmutable $createdUpdatedAt = null
+    ): void {
+        $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+        $firstAuthenticationAt = $firstAuthenticationAt ?? new DateTimeImmutable();
+        $lastAuthenticationAt = $lastAuthenticationAt ?? $firstAuthenticationAt;
+        $count = max($count, 1);
+        $createdUpdatedAt = $createdUpdatedAt ?? new DateTimeImmutable();
+
+        $queryBuilder->insert($this->tableNameConnectedService)
+            ->values(
+                [
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID => ':' .
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT => ':' .
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT => ':' .
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => ':' .
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => ':' .
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => ':' .
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT,
+                ]
+            )
+            ->setParameters(
+                [
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID => $idpSpUserVersionId,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT =>
+                        $firstAuthenticationAt,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT =>
+                        $lastAuthenticationAt,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => $count,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => $createdUpdatedAt,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => $createdUpdatedAt,
+                ],
+                [
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID => Types::BIGINT,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT =>
+                        Types::DATETIMETZ_IMMUTABLE,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT =>
+                        Types::DATETIMETZ_IMMUTABLE,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT => Types::BIGINT,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE,
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT => Types::DATETIMETZ_IMMUTABLE,
+                ]
+            );
+
+        try {
+            $queryBuilder->executeStatement();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing query to insert connected service. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->error(
+                $message,
+                compact('idpSpUserVersionId', 'count', 'firstAuthenticationAt', 'lastAuthenticationAt')
+            );
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function updateConnectedServiceVersionCount(
+        int $connectedServiceId,
+        DateTimeImmutable $happenedAt,
+        int $incrementCountBy = 1
+    ): void {
+        $incrementCountBy = max($incrementCountBy, 1);
+
+        $updateCountQueryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+        $updateCountQueryBuilder->update($this->tableNameConnectedService)
+            ->set(
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT,
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT . ' + ' . $incrementCountBy
+            )
+            ->set(
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT,
+                ':' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT
+            )
+            ->setParameter(
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT,
+                $happenedAt,
+                Types::DATETIMETZ_IMMUTABLE
+            )
+            ->where(
+                $updateCountQueryBuilder->expr()->and(
+                    $updateCountQueryBuilder->expr()->eq(
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_ID,
+                        $updateCountQueryBuilder->createNamedParameter($connectedServiceId, Types::INTEGER)
+                    )
+                )
+            );
+
+        try {
+            $updateCountQueryBuilder->executeStatement();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing update count for connected service. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->error(
+                $message,
+                compact('connectedServiceId', 'happenedAt', 'incrementCountBy')
+            );
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function touchConnectedServiceVersionsTimestamp(
+        int $userId,
+        int $spId,
+        DateTimeImmutable $happenedAt = null
+    ): void {
+        $happenedAt = $happenedAt ?? new DateTimeImmutable();
+
+        $selectConnectedServiceVersionsQueryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+        $selectConnectedServiceVersionsQueryBuilder->select(
+            TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '. ' .
+            TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_ID
+        )
+            ->from($this->tableNameConnectedService, TableConstants::TABLE_ALIAS_CONNECTED_SERVICE)
+            ->innerJoin(
+            //'vcs',
+                TableConstants::TABLE_ALIAS_CONNECTED_SERVICE,
+                //'vds_idp_sp_user_version',
+                $this->tableNameIdpSpUserVersion,
+                //'visuv',
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
+                //'vcs.idp_sp_user_version_id =  visuv.id'
+                TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' .
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID . ' = ' .
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID
+            )->innerJoin(
+            //'visuv',
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
+                //'vds_sp_version',
+                $this->tableNameSpVersion,
+                //'vsv',
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION,
+                //'visuv.sp_version_id = vsv.id'
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID . ' = ' .
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' .
+                BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID
+            )->innerJoin(
+            //'vsv',
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION,
+                //'vds_sp',
+                $this->tableNameSp,
+                //'vs',
+                BaseTableConstants::TABLE_ALIAS_SP,
+                //'vsv.sp_id = vs.id'
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' .
+                BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_SP_ID .
+                ' = ' . BaseTableConstants::TABLE_ALIAS_SP . '.' .
+                BaseTableConstants::TABLE_SP_COLUMN_NAME_ID
+            )->innerJoin(
+            //'visuv',
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
+                //'vds_user_version',
+                $this->tableNameUserVersion,
+                //'vuv',
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION,
+                //'visuv.user_version_id = vuv.id'
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID . ' = ' .
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID
+            )->innerJoin(
+            //'vuv',
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION,
+                //'vds_user',
+                $this->tableNameUser,
+                //'vu',
+                BaseTableConstants::TABLE_ALIAS_USER,
+                //'vuv.user_id = vu.id'
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID . ' = ' .
+                BaseTableConstants::TABLE_ALIAS_USER . '.' .
+                BaseTableConstants::TABLE_USER_COLUMN_NAME_ID
+            )
+            ->where(
+                $selectConnectedServiceVersionsQueryBuilder->expr()->and(
+                    $selectConnectedServiceVersionsQueryBuilder->expr()->eq(
+                    //'vs.id = ' .
+                        BaseTableConstants::TABLE_ALIAS_SP . '.' . BaseTableConstants::TABLE_SP_COLUMN_NAME_ID,
+                        $selectConnectedServiceVersionsQueryBuilder->createNamedParameter($spId)
+                    ),
+                    $selectConnectedServiceVersionsQueryBuilder->expr()->eq(
+                    //'vu.id = ' .
+                        BaseTableConstants::TABLE_ALIAS_USER . '.' . BaseTableConstants::TABLE_USER_COLUMN_NAME_ID,
+                        $selectConnectedServiceVersionsQueryBuilder->createNamedParameter($userId)
+                    )
+                )
+            );
+
+        try {
+            /** @var array<array-key,string> $connectedServiceVersions */
+            $connectedServiceVersions = $selectConnectedServiceVersionsQueryBuilder->executeQuery()->fetchFirstColumn();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error selecting connected service versions. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->error(
+                $message,
+                compact('userId', 'spId', 'happenedAt')
+            );
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+
+        $updateLastAuthenticationAtQueryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+        $updateLastAuthenticationAtQueryBuilder->update($this->tableNameConnectedService)
+            ->set(
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT,
+                ':' . TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT
+            )
+            ->setParameter(
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT,
+                $happenedAt,
+                Types::DATETIMETZ_IMMUTABLE
+            )
+            ->where(
+                $updateLastAuthenticationAtQueryBuilder->expr()->in(
+                    TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_ID,
+                    $connectedServiceVersions
+                )
+            );
+
+        try {
+            $updateLastAuthenticationAtQueryBuilder->executeStatement();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error touching connected service versions. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->error(
+                $message,
+                compact('userId', 'spId', 'happenedAt')
+            );
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function getConnectedServices(string $userIdentifierHashSha256): array
+    {
+        try {
+            $connectedServicesQueryBuilder = $this->connection->dbal()->createQueryBuilder();
+            $lastMetadataAndAttributesQueryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+            /** @psalm-suppress TooManyArguments */
+            $connectedServicesQueryBuilder->select(
+                //'vs.entity_id AS sp_entity_id',
+                BaseTableConstants::TABLE_ALIAS_SP . '.' .
+                BaseTableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID . ' AS ' .
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_ENTITY_ID,
+                //'SUM(vcs.count) AS number_of_authentications',
+                'SUM(' .  TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' .
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT . ') AS ' .
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS,
+                //'MAX(vcs.last_authentication_at) AS last_authentication_at',
+                'MAX(' .  TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' .
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT . ') AS ' .
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT,
+                //'MIN(vcs.first_authentication_at) AS first_authentication_at',
+                'MIN(' .  TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' .
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT . ') AS ' .
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT,
+            )->from($this->tableNameConnectedService, TableConstants::TABLE_ALIAS_CONNECTED_SERVICE)
+            ->innerJoin(
+                //'vcs',
+                TableConstants::TABLE_ALIAS_CONNECTED_SERVICE,
+                //'vds_idp_sp_user_version',
+                $this->tableNameIdpSpUserVersion,
+                //'visuv',
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
+                //'vcs.idp_sp_user_version_id =  visuv.id'
+                TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' .
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID . ' = ' .
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID
+            )->innerJoin(
+                //'visuv',
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
+                //'vds_sp_version',
+                $this->tableNameSpVersion,
+                //'vsv',
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION,
+                //'visuv.sp_version_id = vsv.id'
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID . ' = ' .
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' .
+                BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID
+            )->innerJoin(
+                //'vsv',
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION,
+                //'vds_sp',
+                $this->tableNameSp,
+                //'vs',
+                BaseTableConstants::TABLE_ALIAS_SP,
+                //'vsv.sp_id = vs.id'
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' .
+                BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_SP_ID .
+                ' = ' . BaseTableConstants::TABLE_ALIAS_SP . '.' .
+                BaseTableConstants::TABLE_SP_COLUMN_NAME_ID
+            )->innerJoin(
+                //'visuv',
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
+                //'vds_user_version',
+                $this->tableNameUserVersion,
+                //'vuv',
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION,
+                //'visuv.user_version_id = vuv.id'
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID . ' = ' .
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID
+            )->innerJoin(
+                //'vuv',
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION,
+                //'vds_user',
+                $this->tableNameUser,
+                //'vu',
+                BaseTableConstants::TABLE_ALIAS_USER,
+                //'vuv.user_id = vu.id'
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID . ' = ' .
+                BaseTableConstants::TABLE_ALIAS_USER . '.' .
+                BaseTableConstants::TABLE_USER_COLUMN_NAME_ID
+            )
+            ->where(
+                //'vu.identifier_hash_sha256 = ' .
+                BaseTableConstants::TABLE_ALIAS_USER . '.' .
+                BaseTableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 . ' = ' .
+                $connectedServicesQueryBuilder->createNamedParameter($userIdentifierHashSha256)
+            )->groupBy(
+                //'sp_entity_id'
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_ENTITY_ID
+            )->orderBy(
+                //'number_of_authentications',
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS,
+                'DESC'
+            );
+
+            /** @psalm-suppress TooManyArguments */
+            $lastMetadataAndAttributesQueryBuilder->select(
+                //'vs.entity_id AS sp_entity_id',
+                BaseTableConstants::TABLE_ALIAS_SP . '.' .
+                BaseTableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID . ' AS ' .
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_ENTITY_ID,
+                //'vsv.metadata AS sp_metadata',
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' .
+                BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_METADATA . ' AS ' .
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA,
+                //'vuv.attributes AS user_attributes',
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES . ' AS ' .
+                TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES
+            )->from(
+                //'vds_connected_service',
+                $this->tableNameConnectedService,
+                //'vcs'
+                TableConstants::TABLE_ALIAS_CONNECTED_SERVICE
+            )->innerJoin(
+            //'vcs',
+                TableConstants::TABLE_ALIAS_CONNECTED_SERVICE,
+                //'vds_idp_sp_user_version',
+                $this->tableNameIdpSpUserVersion,
+                //'visuv',
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
+                //'vcs.idp_sp_user_version_id =  visuv.id'
+                TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' .
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID . ' = ' .
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID
+            )->innerJoin(
+            //'visuv',
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
+                //'vds_sp_version',
+                $this->tableNameSpVersion,
+                //'vsv',
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION,
+                //'visuv.sp_version_id = vsv.id'
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID . ' = ' .
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' .
+                BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID
+            )->innerJoin(
+            //'vsv',
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION,
+                //'vds_sp',
+                $this->tableNameSp,
+                //'vs',
+                BaseTableConstants::TABLE_ALIAS_SP,
+                //'vsv.sp_id = vs.id'
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' .
+                BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_SP_ID .
+                ' = ' . BaseTableConstants::TABLE_ALIAS_SP . '.' .
+                BaseTableConstants::TABLE_SP_COLUMN_NAME_ID
+            )->innerJoin(
+            //'visuv',
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION,
+                //'vds_user_version',
+                $this->tableNameUserVersion,
+                //'vuv',
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION,
+                //'visuv.user_version_id = vuv.id'
+                BaseTableConstants::TABLE_ALIAS_IDP_SP_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_USER_VERSION_ID . ' = ' .
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID
+            )->innerJoin(
+            //'vuv',
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION,
+                //'vds_user',
+                $this->tableNameUser,
+                //'vu',
+                BaseTableConstants::TABLE_ALIAS_USER,
+                //'vuv.user_id = vu.id'
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_USER_ID . ' = ' .
+                BaseTableConstants::TABLE_ALIAS_USER . '.' .
+                BaseTableConstants::TABLE_USER_COLUMN_NAME_ID
+            )
+            // SP version join for latest one capability
+            ->leftJoin(
+                //'vsv',
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION,
+                //'vds_sp_version',
+                $this->tableNameSpVersion,
+                //'vsv2',
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION_2, // Another alias for self joining...
+                //'vsv.id = vsv2.id AND vsv.id < vsv2.id' // To be able to get latest one...
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' .
+                BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID . ' = ' .
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION_2 . '.' .
+                BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID . ' AND ' .
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION . '.' .
+                BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID . ' < ' .
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION_2 . '.' .
+                BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID
+            )
+            // User version join for latest one capability
+            ->leftJoin(
+                //'vuv',
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION,
+                //'vds_user_version',
+                $this->tableNameUserVersion,
+                //'vuv2',
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION_2, // Another alias for self joining...
+                //'vuv.id = vuv2.id AND vuv.id < vuv2.id' // To be able to get latest one...
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID . ' = ' .
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION_2 . '.' .
+                BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID . ' AND ' .
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION . '.' .
+                BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID . ' < ' .
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION_2 . '.' .
+                BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID
+            )->where(
+                //'vu.identifier_hash_sha256 = ' .
+                BaseTableConstants::TABLE_ALIAS_USER . '.' .
+                BaseTableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256 . ' = ' .
+                $lastMetadataAndAttributesQueryBuilder->createNamedParameter($userIdentifierHashSha256)
+            )->andWhere(
+                //'vsv2.id IS NULL'
+                BaseTableConstants::TABLE_ALIAS_SP_VERSION_2 . '.' .
+                BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID . ' IS NULL'
+            )->andWhere(
+                //'vuv2.id IS NULL'
+                BaseTableConstants::TABLE_ALIAS_USER_VERSION_2 . '.' .
+                BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID . ' IS NULL'
+            )->orderBy(
+                TableConstants::TABLE_ALIAS_CONNECTED_SERVICE . '.' .
+                TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID,
+                'ASC'
+            );
+
+            $connectedServices = $connectedServicesQueryBuilder->executeQuery()->fetchAllAssociativeIndexed();
+            $lastMetadataAndAttributes = $lastMetadataAndAttributesQueryBuilder
+                ->executeQuery()
+                ->fetchAllAssociativeIndexed();
+
+            return array_merge_recursive($connectedServices, $lastMetadataAndAttributes);
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing query to get connected services. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->error($message, compact('userIdentifierHashSha256'));
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function deleteConnectedServicesOlderThan(DateTimeImmutable $dateTime): void
+    {
+        try {
+            $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+            $queryBuilder->delete($this->tableNameConnectedService)
+                ->where(
+                    $queryBuilder->expr()->lt(
+                        TableConstants::TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT,
+                        $queryBuilder->createNamedParameter($dateTime, Types::DATETIME_IMMUTABLE)
+                    )
+                )->executeStatement();
+        } catch (Throwable $exception) {
+            $message = sprintf(
+                'Error executing query to delete old connected services. Error was: %s.',
+                $exception->getMessage()
+            );
+            $this->logger->error($message);
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+}
diff --git a/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/TableConstants.php b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/TableConstants.php
new file mode 100644
index 0000000..028c4d5
--- /dev/null
+++ b/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/TableConstants.php
@@ -0,0 +1,30 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store;
+
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants
+    as BaseTableConstants;
+
+class TableConstants
+{
+    // Table 'connected_service' (connected organizations).
+    public const TABLE_NAME_CONNECTED_SERVICE = 'connected_service';
+    public const TABLE_ALIAS_CONNECTED_SERVICE = BaseTableConstants::TABLE_PREFIX . 'cs';
+    public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_ID = 'id'; // int
+    public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_IDP_SP_USER_VERSION_ID = 'idp_sp_user_version_id'; // int
+    public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT = 'first_authentication_at'; // datetime
+    public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT = 'last_authentication_at'; // datetime
+    public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_COUNT = 'count'; // int
+    public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_CREATED_AT = 'created_at'; // datetime
+    public const TABLE_CONNECTED_SERVICE_COLUMN_NAME_UPDATED_AT = 'updated_at'; // datetime
+
+    // Entity 'ConnectedService' (service provider) related.
+    public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_ENTITY_ID = 'sp_entity_id';
+    public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS = 'number_of_authentications';
+    public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT = 'last_authentication_at';
+    public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT = 'first_authentication_at';
+    public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA = 'sp_metadata';
+    public const ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES = 'user_attributes';
+}
diff --git a/src/Stores/Bases/AbstractStore.php b/src/Data/Stores/Bases/AbstractStore.php
similarity index 97%
rename from src/Stores/Bases/AbstractStore.php
rename to src/Data/Stores/Bases/AbstractStore.php
index 09b5ff6..d4966c0 100644
--- a/src/Stores/Bases/AbstractStore.php
+++ b/src/Data/Stores/Bases/AbstractStore.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Bases;
+namespace SimpleSAML\Module\accounting\Data\Stores\Bases;
 
 use Psr\Log\LoggerInterface;
 use ReflectionClass;
diff --git a/src/Stores/Bases/DoctrineDbal/AbstractRawEntity.php b/src/Data/Stores/Bases/DoctrineDbal/AbstractRawEntity.php
similarity index 95%
rename from src/Stores/Bases/DoctrineDbal/AbstractRawEntity.php
rename to src/Data/Stores/Bases/DoctrineDbal/AbstractRawEntity.php
index c832bd8..1f3ea88 100644
--- a/src/Stores/Bases/DoctrineDbal/AbstractRawEntity.php
+++ b/src/Data/Stores/Bases/DoctrineDbal/AbstractRawEntity.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal;
+namespace SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal;
 
 use DateTimeImmutable;
 use Doctrine\DBAL\Platforms\AbstractPlatform;
diff --git a/src/Stores/Bases/DoctrineDbal/AbstractStore.php b/src/Data/Stores/Bases/DoctrineDbal/AbstractStore.php
similarity index 88%
rename from src/Stores/Bases/DoctrineDbal/AbstractStore.php
rename to src/Data/Stores/Bases/DoctrineDbal/AbstractStore.php
index 0f992d9..1e352da 100644
--- a/src/Stores/Bases/DoctrineDbal/AbstractStore.php
+++ b/src/Data/Stores/Bases/DoctrineDbal/AbstractStore.php
@@ -2,18 +2,18 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal;
+namespace SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal;
 
 use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator;
 
-abstract class AbstractStore extends \SimpleSAML\Module\accounting\Stores\Bases\AbstractStore
+abstract class AbstractStore extends \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore
 {
     protected Connection $connection;
     protected Migrator $migrator;
diff --git a/src/Stores/Builders/Bases/AbstractStoreBuilder.php b/src/Data/Stores/Builders/Bases/AbstractStoreBuilder.php
similarity index 89%
rename from src/Stores/Builders/Bases/AbstractStoreBuilder.php
rename to src/Data/Stores/Builders/Bases/AbstractStoreBuilder.php
index 45c9321..86b03d6 100644
--- a/src/Stores/Builders/Bases/AbstractStoreBuilder.php
+++ b/src/Data/Stores/Builders/Bases/AbstractStoreBuilder.php
@@ -2,17 +2,17 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Builders\Bases;
+namespace SimpleSAML\Module\accounting\Data\Stores\Builders\Bases;
 
 use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\StoreInterface;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\HelpersManager;
-use SimpleSAML\Module\accounting\Stores\Interfaces\StoreInterface;
 use Throwable;
 
-use function sprintf;
 use function is_subclass_of;
+use function sprintf;
 
 abstract class AbstractStoreBuilder
 {
@@ -48,7 +48,7 @@ abstract class AbstractStoreBuilder
             }
 
             // Build store...
-            /** @var StoreInterface $store */
+            /** @var \SimpleSAML\Module\accounting\Data\Stores\Interfaces\StoreInterface $store */
             $store = $this->helpersManager->getInstanceBuilderUsingModuleConfiguration()->build(
                 $class,
                 $this->moduleConfiguration,
diff --git a/src/Stores/Builders/DataStoreBuilder.php b/src/Data/Stores/Builders/DataStoreBuilder.php
similarity index 74%
rename from src/Stores/Builders/DataStoreBuilder.php
rename to src/Data/Stores/Builders/DataStoreBuilder.php
index 857b447..847e68e 100644
--- a/src/Stores/Builders/DataStoreBuilder.php
+++ b/src/Data/Stores/Builders/DataStoreBuilder.php
@@ -2,13 +2,14 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Builders;
+namespace SimpleSAML\Module\accounting\Data\Stores\Builders;
 
+use SimpleSAML\Module\accounting\Data\Stores\Builders\Bases\AbstractStoreBuilder;
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\DataStoreInterface;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\ModuleConfiguration\ConnectionType;
-use SimpleSAML\Module\accounting\Stores\Interfaces\DataStoreInterface;
 
-class DataStoreBuilder extends Bases\AbstractStoreBuilder
+class DataStoreBuilder extends AbstractStoreBuilder
 {
     /**
      * @throws StoreException
diff --git a/src/Stores/Builders/JobsStoreBuilder.php b/src/Data/Stores/Builders/JobsStoreBuilder.php
similarity index 78%
rename from src/Stores/Builders/JobsStoreBuilder.php
rename to src/Data/Stores/Builders/JobsStoreBuilder.php
index 9ad3784..82704fa 100644
--- a/src/Stores/Builders/JobsStoreBuilder.php
+++ b/src/Data/Stores/Builders/JobsStoreBuilder.php
@@ -2,15 +2,15 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Builders;
+namespace SimpleSAML\Module\accounting\Data\Stores\Builders;
 
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\JobsStoreInterface;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\ModuleConfiguration\ConnectionType;
-use SimpleSAML\Module\accounting\Stores\Interfaces\JobsStoreInterface;
 
 use function sprintf;
 
-class JobsStoreBuilder extends Bases\AbstractStoreBuilder
+class JobsStoreBuilder extends \SimpleSAML\Module\accounting\Data\Stores\Builders\Bases\AbstractStoreBuilder
 {
     /**
      * @throws StoreException
diff --git a/src/Stores/Connections/Bases/AbstractMigrator.php b/src/Data/Stores/Connections/Bases/AbstractMigrator.php
similarity index 96%
rename from src/Stores/Connections/Bases/AbstractMigrator.php
rename to src/Data/Stores/Connections/Bases/AbstractMigrator.php
index ae7e0fe..97cdaa2 100644
--- a/src/Stores/Connections/Bases/AbstractMigrator.php
+++ b/src/Data/Stores/Connections/Bases/AbstractMigrator.php
@@ -2,12 +2,12 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Connections\Bases;
+namespace SimpleSAML\Module\accounting\Data\Stores\Connections\Bases;
 
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\MigrationInterface;
 use SimpleSAML\Module\accounting\Exceptions\InvalidValueException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
 use SimpleSAML\Module\accounting\Services\HelpersManager;
-use SimpleSAML\Module\accounting\Stores\Interfaces\MigrationInterface;
 use Throwable;
 
 abstract class AbstractMigrator
diff --git a/src/Stores/Connections/DoctrineDbal/Bases/AbstractMigration.php b/src/Data/Stores/Connections/DoctrineDbal/Bases/AbstractMigration.php
similarity index 89%
rename from src/Stores/Connections/DoctrineDbal/Bases/AbstractMigration.php
rename to src/Data/Stores/Connections/DoctrineDbal/Bases/AbstractMigration.php
index 9619f19..f4e37ec 100644
--- a/src/Stores/Connections/DoctrineDbal/Bases/AbstractMigration.php
+++ b/src/Data/Stores/Connections/DoctrineDbal/Bases/AbstractMigration.php
@@ -2,13 +2,13 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases;
+namespace SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases;
 
 use Doctrine\DBAL\Schema\AbstractSchemaManager;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\MigrationInterface;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Interfaces\MigrationInterface;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
 use Throwable;
 
 abstract class AbstractMigration implements MigrationInterface
diff --git a/src/Stores/Connections/DoctrineDbal/Connection.php b/src/Data/Stores/Connections/DoctrineDbal/Connection.php
similarity index 91%
rename from src/Stores/Connections/DoctrineDbal/Connection.php
rename to src/Data/Stores/Connections/DoctrineDbal/Connection.php
index 69c6202..de4e520 100644
--- a/src/Stores/Connections/DoctrineDbal/Connection.php
+++ b/src/Data/Stores/Connections/DoctrineDbal/Connection.php
@@ -2,12 +2,12 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal;
+namespace SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal;
 
 use Doctrine\DBAL\DriverManager;
 use Doctrine\DBAL\Exception;
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\ConnectionInterface;
 use SimpleSAML\Module\accounting\Exceptions\InvalidConfigurationException;
-use SimpleSAML\Module\accounting\Stores\Interfaces\ConnectionInterface;
 
 class Connection implements ConnectionInterface
 {
diff --git a/src/Stores/Connections/DoctrineDbal/Factory.php b/src/Data/Stores/Connections/DoctrineDbal/Factory.php
similarity index 92%
rename from src/Stores/Connections/DoctrineDbal/Factory.php
rename to src/Data/Stores/Connections/DoctrineDbal/Factory.php
index b447dbf..e5a7485 100644
--- a/src/Stores/Connections/DoctrineDbal/Factory.php
+++ b/src/Data/Stores/Connections/DoctrineDbal/Factory.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal;
+namespace SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal;
 
 use Psr\Log\LoggerInterface;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
diff --git a/src/Stores/Connections/DoctrineDbal/Migrator.php b/src/Data/Stores/Connections/DoctrineDbal/Migrator.php
similarity index 94%
rename from src/Stores/Connections/DoctrineDbal/Migrator.php
rename to src/Data/Stores/Connections/DoctrineDbal/Migrator.php
index 8046446..ad7f873 100644
--- a/src/Stores/Connections/DoctrineDbal/Migrator.php
+++ b/src/Data/Stores/Connections/DoctrineDbal/Migrator.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal;
+namespace SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal;
 
 use DateTimeImmutable;
 use Doctrine\DBAL\Schema\AbstractSchemaManager;
@@ -11,12 +11,12 @@ use Doctrine\DBAL\Types\Types;
 use Psr\Log\LoggerInterface;
 use ReflectionClass;
 use ReflectionException;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\MigrationInterface;
 use SimpleSAML\Module\accounting\Exceptions\InvalidValueException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Services\HelpersManager;
-use SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
-use SimpleSAML\Module\accounting\Stores\Interfaces\MigrationInterface;
 use Throwable;
 
 class Migrator extends AbstractMigrator
diff --git a/src/Data/Stores/Interfaces/ActivityInterface.php b/src/Data/Stores/Interfaces/ActivityInterface.php
new file mode 100644
index 0000000..1dfd67b
--- /dev/null
+++ b/src/Data/Stores/Interfaces/ActivityInterface.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Interfaces;
+
+use SimpleSAML\Module\accounting\Entities\Activity;
+
+interface ActivityInterface extends DataStoreInterface
+{
+    public function getActivity(string $userIdentifier, int $maxResults, int $firstResult): Activity\Bag;
+}
diff --git a/src/Data/Stores/Interfaces/ConnectedServicesInterface.php b/src/Data/Stores/Interfaces/ConnectedServicesInterface.php
new file mode 100644
index 0000000..9c39afc
--- /dev/null
+++ b/src/Data/Stores/Interfaces/ConnectedServicesInterface.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Interfaces;
+
+use SimpleSAML\Module\accounting\Entities\ConnectedService;
+
+interface ConnectedServicesInterface extends DataStoreInterface
+{
+    public function getConnectedServices(string $userIdentifier): ConnectedService\Bag;
+}
diff --git a/src/Stores/Interfaces/ConnectionInterface.php b/src/Data/Stores/Interfaces/ConnectionInterface.php
similarity index 51%
rename from src/Stores/Interfaces/ConnectionInterface.php
rename to src/Data/Stores/Interfaces/ConnectionInterface.php
index b61d320..b1e5aa9 100644
--- a/src/Stores/Interfaces/ConnectionInterface.php
+++ b/src/Data/Stores/Interfaces/ConnectionInterface.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Interfaces;
+namespace SimpleSAML\Module\accounting\Data\Stores\Interfaces;
 
 interface ConnectionInterface
 {
diff --git a/src/Stores/Interfaces/DataStoreInterface.php b/src/Data/Stores/Interfaces/DataStoreInterface.php
similarity index 61%
rename from src/Stores/Interfaces/DataStoreInterface.php
rename to src/Data/Stores/Interfaces/DataStoreInterface.php
index ce578f4..c025fe8 100644
--- a/src/Stores/Interfaces/DataStoreInterface.php
+++ b/src/Data/Stores/Interfaces/DataStoreInterface.php
@@ -2,13 +2,11 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Interfaces;
+namespace SimpleSAML\Module\accounting\Data\Stores\Interfaces;
 
 use DateTimeImmutable;
 use Psr\Log\LoggerInterface;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event;
-use SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider;
-use SimpleSAML\Module\accounting\Entities\Activity;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 
 interface DataStoreInterface extends StoreInterface
@@ -22,9 +20,5 @@ interface DataStoreInterface extends StoreInterface
 
     public function persist(Event $authenticationEvent): void;
 
-    public function getConnectedOrganizations(string $userIdentifierHashSha256): ConnectedServiceProvider\Bag;
-
-    public function getActivity(string $userIdentifierHashSha256, int $maxResults, int $firstResult): Activity\Bag;
-
     public function deleteDataOlderThan(DateTimeImmutable $dateTime): void;
 }
diff --git a/src/Stores/Interfaces/JobsStoreInterface.php b/src/Data/Stores/Interfaces/JobsStoreInterface.php
similarity index 93%
rename from src/Stores/Interfaces/JobsStoreInterface.php
rename to src/Data/Stores/Interfaces/JobsStoreInterface.php
index 43f9bdb..46c93ec 100644
--- a/src/Stores/Interfaces/JobsStoreInterface.php
+++ b/src/Data/Stores/Interfaces/JobsStoreInterface.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Interfaces;
+namespace SimpleSAML\Module\accounting\Data\Stores\Interfaces;
 
 use Psr\Log\LoggerInterface;
 use SimpleSAML\Module\accounting\Entities\Interfaces\JobInterface;
diff --git a/src/Stores/Interfaces/MigrationInterface.php b/src/Data/Stores/Interfaces/MigrationInterface.php
similarity index 81%
rename from src/Stores/Interfaces/MigrationInterface.php
rename to src/Data/Stores/Interfaces/MigrationInterface.php
index ce7e523..9c0c2f6 100644
--- a/src/Stores/Interfaces/MigrationInterface.php
+++ b/src/Data/Stores/Interfaces/MigrationInterface.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Interfaces;
+namespace SimpleSAML\Module\accounting\Data\Stores\Interfaces;
 
 interface MigrationInterface
 {
diff --git a/src/Stores/Interfaces/StoreInterface.php b/src/Data/Stores/Interfaces/StoreInterface.php
similarity index 89%
rename from src/Stores/Interfaces/StoreInterface.php
rename to src/Data/Stores/Interfaces/StoreInterface.php
index 29b1bd0..3241c9f 100644
--- a/src/Stores/Interfaces/StoreInterface.php
+++ b/src/Data/Stores/Interfaces/StoreInterface.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Interfaces;
+namespace SimpleSAML\Module\accounting\Data\Stores\Interfaces;
 
 use Psr\Log\LoggerInterface;
 use SimpleSAML\Module\accounting\Interfaces\BuildableUsingModuleConfigurationInterface;
diff --git a/src/Stores/Jobs/DoctrineDbal/Store.php b/src/Data/Stores/Jobs/DoctrineDbal/Store.php
similarity index 91%
rename from src/Stores/Jobs/DoctrineDbal/Store.php
rename to src/Data/Stores/Jobs/DoctrineDbal/Store.php
index 0d5f804..e81d222 100644
--- a/src/Stores/Jobs/DoctrineDbal/Store.php
+++ b/src/Data/Stores/Jobs/DoctrineDbal/Store.php
@@ -2,19 +2,19 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal;
+namespace SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal;
 
 use Doctrine\DBAL\Exception;
 use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory;
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\JobsStoreInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Repository;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\TableConstants;
 use SimpleSAML\Module\accounting\Entities\Interfaces\JobInterface;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractStore;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory;
-use SimpleSAML\Module\accounting\Stores\Interfaces\JobsStoreInterface;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Repository;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\TableConstants;
 use Throwable;
 
 class Store extends AbstractStore implements JobsStoreInterface
diff --git a/src/Stores/Jobs/DoctrineDbal/Store/Migrations/Bases/AbstractCreateJobsTable.php b/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Bases/AbstractCreateJobsTable.php
similarity index 86%
rename from src/Stores/Jobs/DoctrineDbal/Store/Migrations/Bases/AbstractCreateJobsTable.php
rename to src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Bases/AbstractCreateJobsTable.php
index d602ec6..b88ac7f 100644
--- a/src/Stores/Jobs/DoctrineDbal/Store/Migrations/Bases/AbstractCreateJobsTable.php
+++ b/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Bases/AbstractCreateJobsTable.php
@@ -2,13 +2,13 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Bases;
+namespace SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Bases;
 
 use Doctrine\DBAL\Schema\Table;
 use Doctrine\DBAL\Types\Types;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\TableConstants;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\TableConstants;
 use Throwable;
 
 use function sprintf;
diff --git a/src/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTable.php b/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTable.php
similarity index 58%
rename from src/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTable.php
rename to src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTable.php
index 838b202..49bbd7f 100644
--- a/src/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTable.php
+++ b/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTable.php
@@ -2,9 +2,9 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations;
+namespace SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations;
 
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store;
 
 class Version20220601000000CreateJobTable extends Store\Migrations\Bases\AbstractCreateJobsTable
 {
diff --git a/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTable.php b/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTable.php
new file mode 100644
index 0000000..0fbd60a
--- /dev/null
+++ b/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTable.php
@@ -0,0 +1,15 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations;
+
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Bases\AbstractCreateJobsTable;
+
+class Version20220601000100CreateJobFailedTable extends AbstractCreateJobsTable
+{
+    protected function getJobsTableName(): string
+    {
+        return 'job_failed';
+    }
+}
diff --git a/src/Stores/Jobs/DoctrineDbal/Store/RawJob.php b/src/Data/Stores/Jobs/DoctrineDbal/Store/RawJob.php
similarity index 60%
rename from src/Stores/Jobs/DoctrineDbal/Store/RawJob.php
rename to src/Data/Stores/Jobs/DoctrineDbal/Store/RawJob.php
index b302fee..1377034 100644
--- a/src/Stores/Jobs/DoctrineDbal/Store/RawJob.php
+++ b/src/Data/Stores/Jobs/DoctrineDbal/Store/RawJob.php
@@ -2,14 +2,14 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
+namespace SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store;
 
 use DateTimeImmutable;
 use Doctrine\DBAL\Platforms\AbstractPlatform;
+use SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store;
 use SimpleSAML\Module\accounting\Entities\Bases\AbstractPayload;
 use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
-use SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractRawEntity;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
 
 use function sprintf;
 
@@ -24,19 +24,19 @@ class RawJob extends AbstractRawEntity
     {
         parent::__construct($rawRow, $abstractPlatform);
 
-        $this->id = (int)$rawRow[Store\TableConstants::COLUMN_NAME_ID];
-        $this->payload = $this->resolvePayload((string)$rawRow[Store\TableConstants::COLUMN_NAME_PAYLOAD]);
-        $this->type = (string)$rawRow[Store\TableConstants::COLUMN_NAME_TYPE];
-        $this->createdAt = $this->resolveDateTimeImmutable($rawRow[Store\TableConstants::COLUMN_NAME_CREATED_AT]);
+        $this->id = (int)$rawRow[TableConstants::COLUMN_NAME_ID];
+        $this->payload = $this->resolvePayload((string)$rawRow[TableConstants::COLUMN_NAME_PAYLOAD]);
+        $this->type = (string)$rawRow[TableConstants::COLUMN_NAME_TYPE];
+        $this->createdAt = $this->resolveDateTimeImmutable($rawRow[TableConstants::COLUMN_NAME_CREATED_AT]);
     }
 
     protected function validate(array $rawRow): void
     {
         $columnsToCheck = [
-            Store\TableConstants::COLUMN_NAME_ID,
-            Store\TableConstants::COLUMN_NAME_PAYLOAD,
-            Store\TableConstants::COLUMN_NAME_TYPE,
-            Store\TableConstants::COLUMN_NAME_CREATED_AT,
+            TableConstants::COLUMN_NAME_ID,
+            TableConstants::COLUMN_NAME_PAYLOAD,
+            TableConstants::COLUMN_NAME_TYPE,
+            TableConstants::COLUMN_NAME_CREATED_AT,
         ];
 
         foreach ($columnsToCheck as $column) {
@@ -45,27 +45,27 @@ class RawJob extends AbstractRawEntity
             }
         }
 
-        if (! is_numeric($rawRow[Store\TableConstants::COLUMN_NAME_ID])) {
+        if (! is_numeric($rawRow[TableConstants::COLUMN_NAME_ID])) {
             throw new UnexpectedValueException(
-                sprintf('Column %s must be numeric.', Store\TableConstants::COLUMN_NAME_ID)
+                sprintf('Column %s must be numeric.', TableConstants::COLUMN_NAME_ID)
             );
         }
 
-        if (! is_string($rawRow[Store\TableConstants::COLUMN_NAME_PAYLOAD])) {
+        if (! is_string($rawRow[TableConstants::COLUMN_NAME_PAYLOAD])) {
             throw new UnexpectedValueException(
-                sprintf('Column %s must be string.', Store\TableConstants::COLUMN_NAME_PAYLOAD)
+                sprintf('Column %s must be string.', TableConstants::COLUMN_NAME_PAYLOAD)
             );
         }
 
-        if (! is_string($rawRow[Store\TableConstants::COLUMN_NAME_TYPE])) {
+        if (! is_string($rawRow[TableConstants::COLUMN_NAME_TYPE])) {
             throw new UnexpectedValueException(
-                sprintf('Column %s must be string.', Store\TableConstants::COLUMN_NAME_TYPE)
+                sprintf('Column %s must be string.', TableConstants::COLUMN_NAME_TYPE)
             );
         }
 
-        if (! is_string($rawRow[Store\TableConstants::COLUMN_NAME_CREATED_AT])) {
+        if (! is_string($rawRow[TableConstants::COLUMN_NAME_CREATED_AT])) {
             throw new UnexpectedValueException(
-                sprintf('Column %s must be string.', Store\TableConstants::COLUMN_NAME_CREATED_AT)
+                sprintf('Column %s must be string.', TableConstants::COLUMN_NAME_CREATED_AT)
             );
         }
     }
diff --git a/src/Stores/Jobs/DoctrineDbal/Store/Repository.php b/src/Data/Stores/Jobs/DoctrineDbal/Store/Repository.php
similarity index 96%
rename from src/Stores/Jobs/DoctrineDbal/Store/Repository.php
rename to src/Data/Stores/Jobs/DoctrineDbal/Store/Repository.php
index 09a17ff..23f0842 100644
--- a/src/Stores/Jobs/DoctrineDbal/Store/Repository.php
+++ b/src/Data/Stores/Jobs/DoctrineDbal/Store/Repository.php
@@ -2,16 +2,16 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
+namespace SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store;
 
 use Doctrine\DBAL\Types\Types;
 use Psr\Log\LoggerInterface;
 use ReflectionClass;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store;
 use SimpleSAML\Module\accounting\Entities\GenericJob;
 use SimpleSAML\Module\accounting\Entities\Interfaces\JobInterface;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
 use Throwable;
 
 class Repository
diff --git a/src/Stores/Jobs/DoctrineDbal/Store/TableConstants.php b/src/Data/Stores/Jobs/DoctrineDbal/Store/TableConstants.php
similarity index 84%
rename from src/Stores/Jobs/DoctrineDbal/Store/TableConstants.php
rename to src/Data/Stores/Jobs/DoctrineDbal/Store/TableConstants.php
index 55f1779..e3ded0c 100644
--- a/src/Stores/Jobs/DoctrineDbal/Store/TableConstants.php
+++ b/src/Data/Stores/Jobs/DoctrineDbal/Store/TableConstants.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
+namespace SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store;
 
 class TableConstants
 {
diff --git a/src/Stores/Jobs/PhpRedis/RedisStore.php b/src/Data/Stores/Jobs/PhpRedis/RedisStore.php
similarity index 96%
rename from src/Stores/Jobs/PhpRedis/RedisStore.php
rename to src/Data/Stores/Jobs/PhpRedis/RedisStore.php
index 0aa6f04..c61b4ec 100644
--- a/src/Stores/Jobs/PhpRedis/RedisStore.php
+++ b/src/Data/Stores/Jobs/PhpRedis/RedisStore.php
@@ -2,16 +2,16 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Jobs\PhpRedis;
+namespace SimpleSAML\Module\accounting\Data\Stores\Jobs\PhpRedis;
 
 use Psr\Log\LoggerInterface;
 use Redis;
+use SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore;
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\JobsStoreInterface;
 use SimpleSAML\Module\accounting\Entities\Interfaces\JobInterface;
 use SimpleSAML\Module\accounting\Exceptions\InvalidConfigurationException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Stores\Bases\AbstractStore;
-use SimpleSAML\Module\accounting\Stores\Interfaces\JobsStoreInterface;
 use Throwable;
 
 class RedisStore extends AbstractStore implements JobsStoreInterface
diff --git a/src/Data/Trackers/Activity/DoctrineDbal/VersionedDataTracker.php b/src/Data/Trackers/Activity/DoctrineDbal/VersionedDataTracker.php
new file mode 100644
index 0000000..8f30682
--- /dev/null
+++ b/src/Data/Trackers/Activity/DoctrineDbal/VersionedDataTracker.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Trackers\Activity\DoctrineDbal;
+
+use DateInterval;
+use DateTimeImmutable;
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Providers\Activity\DoctrineDbal\VersionedDataProvider;
+use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface;
+use SimpleSAML\Module\accounting\Entities\Authentication\Event;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+
+class VersionedDataTracker extends VersionedDataProvider implements DataTrackerInterface
+{
+    /**
+     * @throws StoreException
+     */
+    public static function build(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionType = ModuleConfiguration\ConnectionType::MASTER
+    ): self {
+        return new self($moduleConfiguration, $logger, $connectionType);
+    }
+
+    public function process(Event $authenticationEvent): void
+    {
+        $this->store->persist($authenticationEvent);
+    }
+
+    public function enforceDataRetentionPolicy(DateInterval $retentionPolicy): void
+    {
+        $dateTime = (new DateTimeImmutable())->sub($retentionPolicy);
+
+        $this->store->deleteDataOlderThan($dateTime);
+    }
+}
diff --git a/src/Trackers/Builders/AuthenticationDataTrackerBuilder.php b/src/Data/Trackers/Builders/DataTrackerBuilder.php
similarity index 71%
rename from src/Trackers/Builders/AuthenticationDataTrackerBuilder.php
rename to src/Data/Trackers/Builders/DataTrackerBuilder.php
index 7a58645..51b1654 100644
--- a/src/Trackers/Builders/AuthenticationDataTrackerBuilder.php
+++ b/src/Data/Trackers/Builders/DataTrackerBuilder.php
@@ -2,17 +2,17 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Trackers\Builders;
+namespace SimpleSAML\Module\accounting\Data\Trackers\Builders;
 
 use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface;
 use SimpleSAML\Module\accounting\Exceptions\Exception;
 use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\HelpersManager;
-use SimpleSAML\Module\accounting\Trackers\Interfaces\AuthenticationDataTrackerInterface;
 use Throwable;
 
-class AuthenticationDataTrackerBuilder
+class DataTrackerBuilder
 {
     protected ModuleConfiguration $moduleConfiguration;
     protected LoggerInterface $logger;
@@ -31,22 +31,22 @@ class AuthenticationDataTrackerBuilder
     /**
      * @throws Exception
      */
-    public function build(string $class): AuthenticationDataTrackerInterface
+    public function build(string $class): DataTrackerInterface
     {
         try {
             // Make sure that the class implements proper interface
-            if (!is_subclass_of($class, AuthenticationDataTrackerInterface::class)) {
+            if (!is_subclass_of($class, DataTrackerInterface::class)) {
                 $message = sprintf(
                     'Class %s does not implement interface %s.',
                     $class,
-                    AuthenticationDataTrackerInterface::class
+                    DataTrackerInterface::class
                 );
                 throw new UnexpectedValueException($message);
             }
 
             // Build...
-            /** @var AuthenticationDataTrackerInterface $store */
-            $store = $this->helpersManager->getInstanceBuilderUsingModuleConfiguration()->build(
+            /** @var DataTrackerInterface $tracker */
+            $tracker = $this->helpersManager->getInstanceBuilderUsingModuleConfiguration()->build(
                 $class,
                 $this->moduleConfiguration,
                 $this->logger
@@ -56,6 +56,6 @@ class AuthenticationDataTrackerBuilder
             throw new Exception($message, (int)$exception->getCode(), $exception);
         }
 
-        return $store;
+        return $tracker;
     }
 }
diff --git a/src/Data/Trackers/ConnectedServices/DoctrineDbal/CurrentDataTracker.php b/src/Data/Trackers/ConnectedServices/DoctrineDbal/CurrentDataTracker.php
new file mode 100644
index 0000000..0ff530f
--- /dev/null
+++ b/src/Data/Trackers/ConnectedServices/DoctrineDbal/CurrentDataTracker.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Trackers\ConnectedServices\DoctrineDbal;
+
+use DateInterval;
+use DateTimeImmutable;
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Providers\ConnectedServices\DoctrineDbal\CurrentDataProvider;
+use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface;
+use SimpleSAML\Module\accounting\Entities\Authentication\Event;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+
+class CurrentDataTracker extends CurrentDataProvider implements DataTrackerInterface
+{
+    /**
+     * @throws StoreException
+     */
+    public static function build(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionType = ModuleConfiguration\ConnectionType::MASTER
+    ): self {
+        return new self($moduleConfiguration, $logger, $connectionType);
+    }
+
+    public function process(Event $authenticationEvent): void
+    {
+        $this->store->persist($authenticationEvent);
+    }
+
+    public function enforceDataRetentionPolicy(DateInterval $retentionPolicy): void
+    {
+        $dateTime = (new DateTimeImmutable())->sub($retentionPolicy);
+
+        $this->store->deleteDataOlderThan($dateTime);
+    }
+}
diff --git a/src/Data/Trackers/ConnectedServices/DoctrineDbal/VersionedDataTracker.php b/src/Data/Trackers/ConnectedServices/DoctrineDbal/VersionedDataTracker.php
new file mode 100644
index 0000000..96013b0
--- /dev/null
+++ b/src/Data/Trackers/ConnectedServices/DoctrineDbal/VersionedDataTracker.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Data\Trackers\ConnectedServices\DoctrineDbal;
+
+use DateInterval;
+use DateTimeImmutable;
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Providers\ConnectedServices\DoctrineDbal\VersionedDataProvider;
+use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface;
+use SimpleSAML\Module\accounting\Entities\Authentication\Event;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+
+class VersionedDataTracker extends VersionedDataProvider implements DataTrackerInterface
+{
+    /**
+     * @throws StoreException
+     */
+    public static function build(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        string $connectionType = ModuleConfiguration\ConnectionType::MASTER
+    ): self {
+        return new self($moduleConfiguration, $logger, $connectionType);
+    }
+
+    public function process(Event $authenticationEvent): void
+    {
+        $this->store->persist($authenticationEvent);
+    }
+
+    public function enforceDataRetentionPolicy(DateInterval $retentionPolicy): void
+    {
+        $dateTime = (new DateTimeImmutable())->sub($retentionPolicy);
+
+        $this->store->deleteDataOlderThan($dateTime);
+    }
+}
diff --git a/src/Trackers/Interfaces/AuthenticationDataTrackerInterface.php b/src/Data/Trackers/Interfaces/DataTrackerInterface.php
similarity index 77%
rename from src/Trackers/Interfaces/AuthenticationDataTrackerInterface.php
rename to src/Data/Trackers/Interfaces/DataTrackerInterface.php
index ca0fb42..975e5e3 100644
--- a/src/Trackers/Interfaces/AuthenticationDataTrackerInterface.php
+++ b/src/Data/Trackers/Interfaces/DataTrackerInterface.php
@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Trackers\Interfaces;
+namespace SimpleSAML\Module\accounting\Data\Trackers\Interfaces;
 
 use DateInterval;
 use Psr\Log\LoggerInterface;
@@ -11,7 +11,7 @@ use SimpleSAML\Module\accounting\Interfaces\BuildableUsingModuleConfigurationInt
 use SimpleSAML\Module\accounting\Interfaces\SetupableInterface;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 
-interface AuthenticationDataTrackerInterface extends BuildableUsingModuleConfigurationInterface, SetupableInterface
+interface DataTrackerInterface extends BuildableUsingModuleConfigurationInterface, SetupableInterface
 {
     public static function build(ModuleConfiguration $moduleConfiguration, LoggerInterface $logger): self;
 
diff --git a/src/Entities/Authentication/Event/State/Oidc.php b/src/Entities/Authentication/Event/State/Oidc.php
index b9c384f..a224314 100644
--- a/src/Entities/Authentication/Event/State/Oidc.php
+++ b/src/Entities/Authentication/Event/State/Oidc.php
@@ -31,7 +31,7 @@ class Oidc extends AbstractState
             return $oidcState[self::KEY_OPEN_ID_PROVIDER_METADATA];
         }
 
-        throw new UnexpectedValueException('State array does not contain OpenID Provider metadata.');
+        throw new UnexpectedValueException('State array does not contain OpenID VersionedDataProvider metadata.');
     }
 
     protected function resolveIdentityProviderEntityId(): string
@@ -43,7 +43,7 @@ class Oidc extends AbstractState
             return $this->identityProviderMetadata[Identity\Oidc::METADATA_KEY_ENTITY_ID];
         }
 
-        throw new UnexpectedValueException('OpenID Provider metadata array does not contain issuer.');
+        throw new UnexpectedValueException('OpenID VersionedDataProvider metadata array does not contain issuer.');
     }
 
     protected function resolveServiceProviderMetadata(array $state): array
diff --git a/src/Entities/ConnectedServiceProvider.php b/src/Entities/ConnectedService.php
similarity index 94%
rename from src/Entities/ConnectedServiceProvider.php
rename to src/Entities/ConnectedService.php
index b1ded7a..2a6347b 100644
--- a/src/Entities/ConnectedServiceProvider.php
+++ b/src/Entities/ConnectedService.php
@@ -8,9 +8,9 @@ use DateTimeImmutable;
 use SimpleSAML\Module\accounting\Entities\Interfaces\ServiceProviderInterface;
 
 /**
- * Represents a Service Provider to which a user has authenticated at least once.
+ * Represents a Service VersionedDataProvider to which a user has authenticated at least once.
  */
-class ConnectedServiceProvider
+class ConnectedService
 {
     protected ServiceProviderInterface $serviceProvider;
     protected int $numberOfAuthentications;
diff --git a/src/Entities/ConnectedServiceProvider/Bag.php b/src/Entities/ConnectedService/Bag.php
similarity index 53%
rename from src/Entities/ConnectedServiceProvider/Bag.php
rename to src/Entities/ConnectedService/Bag.php
index 9507d8f..09f468c 100644
--- a/src/Entities/ConnectedServiceProvider/Bag.php
+++ b/src/Entities/ConnectedService/Bag.php
@@ -2,22 +2,22 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider;
+namespace SimpleSAML\Module\accounting\Entities\ConnectedService;
 
-use SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider;
+use SimpleSAML\Module\accounting\Entities\ConnectedService;
 
 class Bag
 {
     /**
-     * @var ConnectedServiceProvider[]
+     * @var ConnectedService[]
      */
     protected array $connectedServiceProviders = [];
 
-    public function addOrReplace(ConnectedServiceProvider $connectedServiceProvider): void
+    public function addOrReplace(ConnectedService $connectedService): void
     {
-        $spEntityId = $connectedServiceProvider->getServiceProvider()->getEntityId();
+        $spEntityId = $connectedService->getServiceProvider()->getEntityId();
 
-        $this->connectedServiceProviders[$spEntityId] = $connectedServiceProvider;
+        $this->connectedServiceProviders[$spEntityId] = $connectedService;
     }
 
     public function getAll(): array
diff --git a/src/Entities/Providers/Identity/Oidc.php b/src/Entities/Providers/Identity/Oidc.php
index d326113..c662f78 100644
--- a/src/Entities/Providers/Identity/Oidc.php
+++ b/src/Entities/Providers/Identity/Oidc.php
@@ -36,7 +36,7 @@ class Oidc extends AbstractProvider implements IdentityProviderInterface
             return $this->metadata[self::METADATA_KEY_ENTITY_ID];
         }
 
-        throw new MetadataException('OpenID Provider metadata does not contain entity ID.');
+        throw new MetadataException('OpenID VersionedDataProvider metadata does not contain entity ID.');
     }
 
     public function getProtocol(): AuthenticationProtocolInterface
diff --git a/src/Entities/Providers/Service/Oidc.php b/src/Entities/Providers/Service/Oidc.php
index 382eabd..5f5ff82 100644
--- a/src/Entities/Providers/Service/Oidc.php
+++ b/src/Entities/Providers/Service/Oidc.php
@@ -38,7 +38,7 @@ class Oidc extends AbstractProvider implements ServiceProviderInterface
             return $this->metadata[self::METADATA_KEY_ENTITY_ID];
         }
 
-        throw new MetadataException('Relying Provider metadata does not contain entity ID.');
+        throw new MetadataException('Relying VersionedDataProvider metadata does not contain entity ID.');
     }
 
     public function getProtocol(): AuthenticationProtocolInterface
diff --git a/src/Http/Controllers/Admin/Configuration.php b/src/Http/Controllers/Admin/Configuration.php
index cd5d953..b1b2433 100644
--- a/src/Http/Controllers/Admin/Configuration.php
+++ b/src/Http/Controllers/Admin/Configuration.php
@@ -7,11 +7,12 @@ namespace SimpleSAML\Module\accounting\Http\Controllers\Admin;
 use Exception;
 use Psr\Log\LoggerInterface;
 use SimpleSAML\Configuration as SspConfiguration;
+use SimpleSAML\Module\accounting\Data\Providers\Builders\DataProviderBuilder;
+use SimpleSAML\Module\accounting\Data\Stores\Builders\JobsStoreBuilder;
+use SimpleSAML\Module\accounting\Data\Trackers\Builders\DataTrackerBuilder;
 use SimpleSAML\Module\accounting\Helpers\Routes;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\HelpersManager;
-use SimpleSAML\Module\accounting\Stores\Builders\JobsStoreBuilder;
-use SimpleSAML\Module\accounting\Trackers\Builders\AuthenticationDataTrackerBuilder;
 use SimpleSAML\Session;
 use SimpleSAML\Utils;
 use SimpleSAML\XHTML\Template;
@@ -59,7 +60,7 @@ class Configuration
         $moduleConfiguration = null;
         $configurationValidationErrors = null;
         $jobsStore = null;
-        $defaultDataTrackerAndProvider = null;
+        $providers = [];
         $additionalTrackers = [];
         $setupNeeded = false;
         $runSetup = $request->query->has('runSetup');
@@ -67,16 +68,17 @@ class Configuration
         try {
             $moduleConfiguration = new ModuleConfiguration();
 
-            $defaultDataTrackerAndProvider =
-                (new AuthenticationDataTrackerBuilder($moduleConfiguration, $this->logger, $this->helpersManager))
-                ->build($moduleConfiguration->getDefaultDataTrackerAndProviderClass());
-
-            if ($defaultDataTrackerAndProvider->needsSetup()) {
-                if ($runSetup) {
-                    $defaultDataTrackerAndProvider->runSetup();
-                } else {
-                    $setupNeeded = true;
+            $dataProviderBuilder = new DataProviderBuilder($moduleConfiguration, $this->logger, $this->helpersManager);
+            foreach ($moduleConfiguration->getProviderClasses() as $providerClass) {
+                $providerInstance = $dataProviderBuilder->build($providerClass);
+                if ($providerInstance->needsSetup()) {
+                    if ($runSetup) {
+                        $providerInstance->runSetup();
+                    } else {
+                        $setupNeeded = true;
+                    }
                 }
+                $providers[$providerClass] = $providerInstance;
             }
 
             if (
@@ -94,19 +96,18 @@ class Configuration
                 }
             }
 
+            $dataTrackerBuilder = new DataTrackerBuilder($moduleConfiguration, $this->logger, $this->helpersManager);
             foreach ($moduleConfiguration->getAdditionalTrackers() as $trackerClass) {
-                $additionalTrackerInstance =
-                    (new AuthenticationDataTrackerBuilder($moduleConfiguration, $this->logger, $this->helpersManager))
-                    ->build($trackerClass);
+                $trackerInstance = $dataTrackerBuilder->build($trackerClass);
 
-                if ($additionalTrackerInstance->needsSetup()) {
+                if ($trackerInstance->needsSetup()) {
                     if ($runSetup) {
-                        $additionalTrackerInstance->runSetup();
+                        $trackerInstance->runSetup();
                     } else {
                         $setupNeeded = true;
                     }
                 }
-                $additionalTrackers[$trackerClass] = $additionalTrackerInstance;
+                $additionalTrackers[$trackerClass] = $trackerInstance;
             }
         } catch (Throwable $exception) {
             $configurationValidationErrors = $exception->getMessage();
@@ -116,11 +117,12 @@ class Configuration
             'moduleConfiguration' => $moduleConfiguration,
             'configurationValidationErrors' => $configurationValidationErrors,
             'jobsStore' => $jobsStore,
-            'defaultDataTrackerAndProvider' => $defaultDataTrackerAndProvider,
+            'providers' => $providers,
             'additionalTrackers' => $additionalTrackers,
             'setupNeeded' => $setupNeeded,
             'profilePageUri' => $this->helpersManager->getRoutes()
                 ->getUrl(Routes::PATH_USER_PERSONAL_DATA),
+            'runSetup' => $runSetup,
         ];
 
         $template = new Template($this->sspConfiguration, 'accounting:admin/configuration/status.twig');
diff --git a/src/Http/Controllers/User/Profile.php b/src/Http/Controllers/User/Profile.php
index c144a97..09dd023 100644
--- a/src/Http/Controllers/User/Profile.php
+++ b/src/Http/Controllers/User/Profile.php
@@ -11,23 +11,28 @@ use SimpleSAML\Error\ConfigurationError;
 use SimpleSAML\Error\CriticalConfigurationError;
 use SimpleSAML\HTTP\RunnableResponse;
 use SimpleSAML\Locale\Translate;
+use SimpleSAML\Metadata\MetaDataStorageHandler;
+use SimpleSAML\Module\accounting\Data\Providers\Builders\DataProviderBuilder;
+use SimpleSAML\Module\accounting\Data\Providers\Interfaces\ActivityInterface;
+use SimpleSAML\Module\accounting\Data\Providers\Interfaces\DataProviderInterface;
 use SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Oidc;
-use SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider;
+use SimpleSAML\Module\accounting\Entities\ConnectedService;
 use SimpleSAML\Module\accounting\Entities\User;
 use SimpleSAML\Module\accounting\Exceptions\Exception;
 use SimpleSAML\Module\accounting\Exceptions\InvalidConfigurationException;
 use SimpleSAML\Module\accounting\Helpers\Attributes;
+use SimpleSAML\Module\accounting\Helpers\ProviderResolver;
 use SimpleSAML\Module\accounting\Helpers\Routes;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\ModuleConfiguration\ConnectionType;
-use SimpleSAML\Module\accounting\Providers\Builders\AuthenticationDataProviderBuilder;
-use SimpleSAML\Module\accounting\Providers\Interfaces\AuthenticationDataProviderInterface;
 use SimpleSAML\Module\accounting\Services\AlertsBag;
 use SimpleSAML\Module\accounting\Services\CsrfToken;
 use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Module\accounting\Services\MenuManager;
 use SimpleSAML\Module\accounting\Services\SspModuleManager;
+use SimpleSAML\Module\oidc\Services\OidcOpenIdProviderMetadataService;
 use SimpleSAML\Session;
+use SimpleSAML\Utils\Config\Metadata;
 use SimpleSAML\XHTML\Template;
 use Symfony\Component\HttpFoundation\Cookie;
 use Symfony\Component\HttpFoundation\JsonResponse;
@@ -46,7 +51,7 @@ class Profile
     protected LoggerInterface $logger;
     protected string $defaultAuthenticationSource;
     protected Simple $authSimple;
-    protected AuthenticationDataProviderBuilder $authenticationDataProviderBuilder;
+    protected DataProviderBuilder $dataProviderBuilder;
     protected HelpersManager $helpersManager;
     protected SspModuleManager $sspModuleManager;
     protected User $user;
@@ -60,7 +65,7 @@ class Profile
      * @param Session $session The current user session.
      * @param LoggerInterface $logger
      * @param Simple|null $authSimple
-     * @param AuthenticationDataProviderBuilder|null $authenticationDataProviderBuilder
+     * @param DataProviderBuilder|null $authenticationDataProviderBuilder
      * @param HelpersManager|null $helpersManager
      * @param SspModuleManager|null $sspModuleManager
      * @param CsrfToken|null $csrfToken
@@ -72,7 +77,7 @@ class Profile
         Session $session,
         LoggerInterface $logger,
         Simple $authSimple = null,
-        AuthenticationDataProviderBuilder $authenticationDataProviderBuilder = null,
+        DataProviderBuilder $authenticationDataProviderBuilder = null,
         HelpersManager $helpersManager = null,
         SspModuleManager $sspModuleManager = null,
         CsrfToken $csrfToken = null,
@@ -88,8 +93,8 @@ class Profile
 
         $this->helpersManager = $helpersManager ?? new HelpersManager();
 
-        $this->authenticationDataProviderBuilder = $authenticationDataProviderBuilder ??
-            new AuthenticationDataProviderBuilder($this->moduleConfiguration, $this->logger, $this->helpersManager);
+        $this->dataProviderBuilder = $authenticationDataProviderBuilder ??
+            new DataProviderBuilder($this->moduleConfiguration, $this->logger, $this->helpersManager);
 
         $this->sspModuleManager = $sspModuleManager ?? new SspModuleManager($this->logger, $this->helpersManager);
 
@@ -132,13 +137,21 @@ class Profile
      * @throws Exception
      * @throws ConfigurationError
      */
-    public function connectedOrganizations(): Template
+    public function connectedOrganizations(): Response
     {
         $userIdentifier = $this->resolveUserIdentifier();
 
-        $authenticationDataProvider = $this->resolveAuthenticationDataProvider();
+        $connectedServiceProviderClass = $this->moduleConfiguration->getConnectedServicesProviderClass();
 
-        $connectedServiceProviderBag = $authenticationDataProvider->getConnectedServiceProviders($userIdentifier);
+        if (is_null($connectedServiceProviderClass)) {
+            return new RedirectResponse($this->helpersManager->getRoutes()->getUrl(Routes::PATH_USER_PERSONAL_DATA));
+        }
+
+        $connectedServicesDataProvider = $this->dataProviderBuilder->buildConnectedServicesProvider(
+            $connectedServiceProviderClass
+        );
+
+        $connectedServiceProviderBag = $connectedServicesDataProvider->getConnectedServices($userIdentifier);
 
         $oidc = $this->sspModuleManager->getOidc();
         $accessTokensByClient = [];
@@ -149,13 +162,13 @@ class Profile
         if ($oidc->isEnabled()) {
             // Filter out OIDC service providers and get their entity (client) IDs.
             $oidcClientIds = array_map(
-                function (ConnectedServiceProvider $connectedServiceProvider) {
-                    return $connectedServiceProvider->getServiceProvider()->getEntityId();
+                function (ConnectedService $connectedService) {
+                    return $connectedService->getServiceProvider()->getEntityId();
                 },
                 array_filter(
                     $connectedServiceProviderBag->getAll(),
-                    function (ConnectedServiceProvider $connectedServiceProvider) {
-                        return $connectedServiceProvider->getServiceProvider()->getProtocol()->getDesignation() ===
+                    function (ConnectedService $connectedService) {
+                        return $connectedService->getServiceProvider()->getProtocol()->getDesignation() ===
                             Oidc::DESIGNATION;
                     }
                 )
@@ -190,18 +203,24 @@ class Profile
      * @throws Exception
      * @throws ConfigurationError
      */
-    public function activity(Request $request): Template
+    public function activity(Request $request): Response
     {
         $userIdentifier = $this->resolveUserIdentifier();
 
-        $authenticationDataProvider = $this->resolveAuthenticationDataProvider();
+        $activityProviderClass = $this->moduleConfiguration->getActivityProviderClass();
+
+        if (is_null($activityProviderClass)) {
+            return new RedirectResponse($this->helpersManager->getRoutes()->getUrl(Routes::PATH_USER_PERSONAL_DATA));
+        }
+
+        $activityDataProvider = $this->dataProviderBuilder->buildActivityProvider($activityProviderClass);
 
         $page = ($page = (int)$request->query->get('page', 1)) > 0 ? $page : 1;
 
         $maxResults = 10;
         $firstResult = ($page - 1) * $maxResults;
 
-        $activityBag = $authenticationDataProvider->getActivity($userIdentifier, $maxResults, $firstResult);
+        $activityBag = $activityDataProvider->getActivity($userIdentifier, $maxResults, $firstResult);
 
         $template = $this->resolveTemplate('accounting:user/activity.twig');
         $template->data += compact('activityBag', 'page', 'maxResults');
@@ -209,98 +228,8 @@ class Profile
         return $template;
     }
 
-    /**
-     * @throws Exception|ConfigurationError
-     */
-    public function oidcTokens(): Response
-    {
-        $oidc = $this->sspModuleManager->getOidc();
-
-        // If oidc module is not enabled, this route should not be called.
-        if (!$oidc->isEnabled()) {
-            return new RedirectResponse($this->helpersManager->getRoutes()->getUrl(Routes::PATH_USER_PERSONAL_DATA));
-        }
-
-        $userIdentifier = $this->resolveUserIdentifier();
-
-        $accessTokensByClient = $this->helpersManager->getArr()->groupByValue(
-            $oidc->getUsersAccessTokens($userIdentifier),
-            'client_id'
-        );
-
-        $refreshTokensByClient = $this->helpersManager->getArr()->groupByValue(
-            $oidc->getUsersRefreshTokens($userIdentifier),
-            'client_id'
-        );
-
-        $clientIds = array_unique(array_merge(array_keys($accessTokensByClient), array_keys($refreshTokensByClient)));
-        $clients = $this->helpersManager->getArr()->groupByValue(
-            $oidc->getClients($clientIds),
-            'id'
-        );
-
-        //die(var_dump($accessTokensByClient, $refreshTokensByClient, $clientIds, $clients));
-
-        $template = $this->resolveTemplate('accounting:user/oidc-tokens.twig');
-        $template->data += compact('accessTokensByClient', 'refreshTokensByClient', 'clients');
-
-        return $template;
-    }
-
-    public function oidcTokenRevoke(Request $request): Response
-    {
-        $oidc = $this->sspModuleManager->getOidc();
-
-        // If oidc module is not enabled, this route should not be called.
-        if (! $oidc->isEnabled()) {
-            return new RedirectResponse($this->helpersManager->getRoutes()->getUrl(Routes::PATH_USER_PERSONAL_DATA));
-        }
-
-        $redirectTo = (string) $request->query->get(Routes::QUERY_REDIRECT_TO_PATH, Routes::PATH_USER_OIDC_TOKENS);
-
-        $response = new RedirectResponse(
-            $this->helpersManager->getRoutes()->getUrl($redirectTo)
-        );
-
-        if (! $this->csrfToken->validate($request->request->getAlnum('csrf-token'))) {
-            $this->alertsBag->put(
-                new AlertsBag\Alert('Could not verify CSRF token.', 'warning')
-            );
-
-            return $response;
-        }
-
-        $validTokenTypes = ['access', 'refresh'];
-
-        $tokenType = $request->request->getAlnum('token-type');
-
-        if (! in_array($tokenType, $validTokenTypes)) {
-            $this->alertsBag->put(
-                new AlertsBag\Alert('Token type not valid.', 'warning')
-            );
-            return $response;
-        }
-
-        $tokenId = $request->request->getAlnum('token-id');
-
-        $userIdentifier = $this->resolveUserIdentifier();
-
-        if ($tokenType === 'access') {
-            $oidc->revokeUsersAccessToken($userIdentifier, $tokenId);
-        } elseif ($tokenType === 'refresh') {
-            $oidc->revokeUsersRefreshToken($userIdentifier, $tokenId);
-        }
-
-        $this->alertsBag->put(
-            new AlertsBag\Alert('Token revoked successfully.', 'success')
-        );
-
-        return $response;
-    }
-
     public function oidcTokenRevokeXhr(Request $request): Response
     {
-
         $oidc = $this->sspModuleManager->getOidc();
         $response = new JsonResponse();
 
@@ -359,18 +288,6 @@ class Profile
         return $userIdentifier;
     }
 
-    /**
-     * @throws Exception
-     */
-    protected function resolveAuthenticationDataProvider(): AuthenticationDataProviderInterface
-    {
-        return $this->authenticationDataProviderBuilder
-            ->build(
-                $this->moduleConfiguration->getDefaultDataTrackerAndProviderClass(),
-                ConnectionType::SLAVE
-            );
-    }
-
     public function logout(): Response
     {
         return new RunnableResponse([$this->authSimple, 'logout'], [$this->getLogoutUrl()]);
@@ -428,25 +345,25 @@ class Profile
                 'personal-data',
                 Translate::noop('Personal Data'),
                 'css/src/icons/prof-page.svg'
-            ),
-            new MenuManager\MenuItem(
-                'connected-organizations',
-                Translate::noop('Connected Organizations'),
-                'css/src/icons/conn-orgs.svg'
-            ),
-            new MenuManager\MenuItem(
-                'activity',
-                Translate::noop('Activity'),
-                'css/src/icons/activity.svg'
             )
         );
 
-        // Depending on other functionalities, add additional menu items.
-        if ($this->sspModuleManager->getOidc()->isEnabled()) {
+        // Depending on enabled functionalities, add additional menu items.
+        if ($this->moduleConfiguration->getConnectedServicesProviderClass() !== null) {
+            $menuManager->addItems(
+                new MenuManager\MenuItem(
+                    'connected-organizations',
+                    Translate::noop('Connected Organizations'),
+                    'css/src/icons/conn-orgs.svg'
+                )
+            );
+        }
+
+        if ($this->moduleConfiguration->getActivityProviderClass() !== null) {
             $menuManager->addItems(
                 new MenuManager\MenuItem(
-                    'oidc-tokens',
-                    Translate::noop('Tokens'),
+                    'activity',
+                    Translate::noop('Activity'),
                     'css/src/icons/activity.svg'
                 )
             );
diff --git a/src/ModuleConfiguration.php b/src/ModuleConfiguration.php
index e4e9e13..7926be5 100644
--- a/src/ModuleConfiguration.php
+++ b/src/ModuleConfiguration.php
@@ -7,12 +7,12 @@ namespace SimpleSAML\Module\accounting;
 use DateInterval;
 use Exception;
 use SimpleSAML\Configuration;
+use SimpleSAML\Module\accounting\Data\Providers\Interfaces\DataProviderInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\JobsStoreInterface;
+use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface;
 use SimpleSAML\Module\accounting\Exceptions\InvalidConfigurationException;
 use SimpleSAML\Module\accounting\ModuleConfiguration\AccountingProcessingType;
 use SimpleSAML\Module\accounting\ModuleConfiguration\ConnectionType;
-use SimpleSAML\Module\accounting\Providers\Interfaces\AuthenticationDataProviderInterface;
-use SimpleSAML\Module\accounting\Stores\Interfaces\JobsStoreInterface;
-use SimpleSAML\Module\accounting\Trackers\Interfaces\AuthenticationDataTrackerInterface;
 use Throwable;
 
 class ModuleConfiguration
@@ -20,7 +20,7 @@ class ModuleConfiguration
     public const MODULE_NAME = 'accounting';
 
     /**
-     * Default file name for module configuration. Can be overridden, for example, for testing purposes.
+     * Default file name for module configuration. Can be overridden in constructor, for example, for testing purposes.
      */
     public const FILE_NAME = 'module_accounting.php';
 
@@ -28,7 +28,6 @@ class ModuleConfiguration
     public const OPTION_DEFAULT_AUTHENTICATION_SOURCE = 'default_authentication_source';
     public const OPTION_ACCOUNTING_PROCESSING_TYPE = 'accounting_processing_type';
     public const OPTION_JOBS_STORE = 'jobs_store';
-    public const OPTION_DEFAULT_DATA_TRACKER_AND_PROVIDER = 'default_data_tracker_and_provider';
     public const OPTION_ADDITIONAL_TRACKERS = 'additional_trackers';
     public const OPTION_CONNECTIONS_AND_PARAMETERS = 'connections_and_parameters';
     public const OPTION_CLASS_TO_CONNECTION_MAP = 'class_to_connection_map';
@@ -38,6 +37,8 @@ class ModuleConfiguration
         'job_runner_should_pause_after_number_of_jobs_processed';
     public const OPTION_TRACKER_DATA_RETENTION_POLICY = 'tracker_data_retention_policy';
     public const OPTION_CRON_TAG_FOR_TRACKER_DATA_RETENTION_POLICY = 'cron_tag_for_tracker_data_retention_policy';
+    public const OPTION_PROVIDER_FOR_CONNECTED_SERVICES = 'provider_for_connected_services';
+    public const OPTION_PROVIDER_FOR_ACTIVITY = 'provider_for_activity';
 
     /**
      * Contains configuration from module configuration file.
@@ -132,9 +133,25 @@ class ModuleConfiguration
         return $value;
     }
 
-    public function getDefaultDataTrackerAndProviderClass(): string
+    public function getConnectedServicesProviderClass(): ?string
     {
-        return $this->getConfiguration()->getString(self::OPTION_DEFAULT_DATA_TRACKER_AND_PROVIDER);
+        return $this->getConfiguration()->getOptionalString(self::OPTION_PROVIDER_FOR_CONNECTED_SERVICES, null);
+    }
+
+    public function getActivityProviderClass(): ?string
+    {
+        return $this->getConfiguration()->getOptionalString(self::OPTION_PROVIDER_FOR_ACTIVITY, null);
+    }
+
+    /**
+     * @return array<string>
+     */
+    public function getProviderClasses(): array
+    {
+        return array_filter([
+            $this->getConnectedServicesProviderClass(),
+            $this->getActivityProviderClass(),
+        ]);
     }
 
     public function getConnectionsAndParameters(): array
@@ -280,7 +297,7 @@ class ModuleConfiguration
         }
 
         try {
-            $this->validateDefaultDataTrackerAndProvider();
+            $this->validateDataProviders();
         } catch (Throwable $exception) {
             $errors[] = $exception->getMessage();
         }
@@ -313,27 +330,18 @@ class ModuleConfiguration
     /**
      * @throws InvalidConfigurationException
      */
-    protected function validateDefaultDataTrackerAndProvider(): void
+    protected function validateDataProviders(): void
     {
         $errors = [];
 
-        // Default data tracker and provider must implement proper interfaces.
-        $defaultDataTrackerAndProviderClass = $this->getDefaultDataTrackerAndProviderClass();
-
-        if (!is_subclass_of($defaultDataTrackerAndProviderClass, AuthenticationDataTrackerInterface::class)) {
-            $errors[] = sprintf(
-                'Default authentication data tracker and provider class \'%s\' does not implement interface \'%s\'.',
-                $defaultDataTrackerAndProviderClass,
-                AuthenticationDataTrackerInterface::class
-            );
-        }
-
-        if (!is_subclass_of($defaultDataTrackerAndProviderClass, AuthenticationDataProviderInterface::class)) {
-            $errors[] = sprintf(
-                'Default authentication data tracker and provider class \'%s\' does not implement interface \'%s\'.',
-                $defaultDataTrackerAndProviderClass,
-                AuthenticationDataProviderInterface::class
-            );
+        foreach ($this->getProviderClasses() as $providerClass) {
+            if (!is_subclass_of($providerClass, DataProviderInterface::class)) {
+                $errors[] = sprintf(
+                    'VersionedDataProvider class \'%s\' does not implement interface \'%s\'.',
+                    $providerClass,
+                    DataProviderInterface::class
+                );
+            }
         }
 
         if (!empty($errors)) {
@@ -353,11 +361,11 @@ class ModuleConfiguration
             /** @psalm-suppress DocblockTypeContradiction */
             if (!is_string($trackerClass)) {
                 $errors[] = 'Additional trackers array must contain class strings only.';
-            } elseif (!is_subclass_of($trackerClass, AuthenticationDataTrackerInterface::class)) {
+            } elseif (!is_subclass_of($trackerClass, DataTrackerInterface::class)) {
                 $errors[] = sprintf(
-                    'Tracker class \'%s\' does not implement interface \'%s\'.',
+                    'VersionedDataTracker class \'%s\' does not implement interface \'%s\'.',
                     $trackerClass,
-                    AuthenticationDataTrackerInterface::class
+                    DataTrackerInterface::class
                 );
             }
         }
diff --git a/src/Providers/Builders/AuthenticationDataProviderBuilder.php b/src/Providers/Builders/AuthenticationDataProviderBuilder.php
deleted file mode 100644
index 1bc13f3..0000000
--- a/src/Providers/Builders/AuthenticationDataProviderBuilder.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace SimpleSAML\Module\accounting\Providers\Builders;
-
-use Psr\Log\LoggerInterface;
-use SimpleSAML\Module\accounting\Exceptions\Exception;
-use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
-use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Providers\Interfaces\AuthenticationDataProviderInterface;
-use SimpleSAML\Module\accounting\Services\HelpersManager;
-use Throwable;
-
-class AuthenticationDataProviderBuilder
-{
-    protected ModuleConfiguration $moduleConfiguration;
-    protected LoggerInterface $logger;
-    protected HelpersManager $helpersManager;
-
-    public function __construct(
-        ModuleConfiguration $moduleConfiguration,
-        LoggerInterface $logger,
-        HelpersManager $helpersManager
-    ) {
-        $this->moduleConfiguration = $moduleConfiguration;
-        $this->logger = $logger;
-        $this->helpersManager = $helpersManager;
-    }
-
-    /**
-     * @throws Exception
-     */
-    public function build(
-        string $class,
-        string $connectionType = ModuleConfiguration\ConnectionType::MASTER
-    ): AuthenticationDataProviderInterface {
-        try {
-            // Make sure that the class implements proper interface
-            if (!is_subclass_of($class, AuthenticationDataProviderInterface::class)) {
-                $message = sprintf(
-                    'Class %s does not implement interface %s.',
-                    $class,
-                    AuthenticationDataProviderInterface::class
-                );
-                throw new UnexpectedValueException($message);
-            }
-
-            // Build...
-            /** @var AuthenticationDataProviderInterface $store */
-            $store = $this->helpersManager->getInstanceBuilderUsingModuleConfiguration()->build(
-                $class,
-                $this->moduleConfiguration,
-                $this->logger,
-                [$connectionType]
-            );
-        } catch (Throwable $exception) {
-            $message = sprintf('Error building instance for class %s. Error was: %s', $class, $exception->getMessage());
-            throw new Exception($message, (int)$exception->getCode(), $exception);
-        }
-
-        return $store;
-    }
-}
diff --git a/src/Services/JobRunner.php b/src/Services/JobRunner.php
index 12b47e2..f73796c 100644
--- a/src/Services/JobRunner.php
+++ b/src/Services/JobRunner.php
@@ -11,6 +11,8 @@ use Psr\Log\LoggerInterface;
 use Psr\SimpleCache\CacheInterface;
 use Psr\SimpleCache\InvalidArgumentException;
 use SimpleSAML\Configuration as SspConfiguration;
+use SimpleSAML\Module\accounting\Data\Stores\Builders\JobsStoreBuilder;
+use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event\Job;
 use SimpleSAML\Module\accounting\Exceptions\Exception;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
@@ -18,9 +20,6 @@ use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\JobRunner\RateLimiter;
 use SimpleSAML\Module\accounting\Services\JobRunner\State;
-use SimpleSAML\Module\accounting\Stores\Builders\JobsStoreBuilder;
-use SimpleSAML\Module\accounting\Trackers\Builders\AuthenticationDataTrackerBuilder;
-use SimpleSAML\Module\accounting\Trackers\Interfaces\AuthenticationDataTrackerInterface;
 use Throwable;
 
 class JobRunner
@@ -28,7 +27,6 @@ class JobRunner
     protected ModuleConfiguration $moduleConfiguration;
     protected SspConfiguration $sspConfiguration;
     protected LoggerInterface $logger;
-    protected AuthenticationDataTrackerBuilder $authenticationDataTrackerBuilder;
     protected JobsStoreBuilder $jobsStoreBuilder;
     protected CacheInterface $cache;
     protected State $state;
@@ -49,6 +47,7 @@ class JobRunner
     protected DateInterval $stateStaleThresholdInterval;
     protected RateLimiter $rateLimiter;
     protected HelpersManager $helpersManager;
+    protected TrackerResolver $trackerResolver;
     protected ?DateInterval $maximumExecutionTime;
     protected ?int $shouldPauseAfterNumberOfJobsProcessed;
 
@@ -61,7 +60,7 @@ class JobRunner
         SspConfiguration $sspConfiguration,
         LoggerInterface $logger = null,
         HelpersManager $helpersManager = null,
-        AuthenticationDataTrackerBuilder $authenticationDataTrackerBuilder = null,
+        TrackerResolver $trackerResolver = null,
         JobsStoreBuilder $jobsStoreBuilder = null,
         CacheInterface $cache = null,
         State $state = null,
@@ -72,8 +71,8 @@ class JobRunner
         $this->logger = $logger ?? new Logger();
         $this->helpersManager = $helpersManager ?? new HelpersManager();
 
-        $this->authenticationDataTrackerBuilder = $authenticationDataTrackerBuilder ??
-            new AuthenticationDataTrackerBuilder($this->moduleConfiguration, $this->logger, $this->helpersManager);
+        $this->trackerResolver = $trackerResolver ??
+            new TrackerResolver($this->moduleConfiguration, $this->logger, $this->helpersManager);
         $this->jobsStoreBuilder = $jobsStoreBuilder ??
             new JobsStoreBuilder($this->moduleConfiguration, $this->logger, $this->helpersManager);
 
@@ -83,7 +82,7 @@ class JobRunner
 
         $this->state = $state ?? new State($this->jobRunnerId);
 
-        $this->trackers = $this->resolveTrackers();
+        $this->trackers = $this->trackerResolver->fromModuleConfiguration();
         $this->stateStaleThresholdInterval = new DateInterval(self::STATE_STALE_THRESHOLD_INTERVAL);
         $this->rateLimiter = $rateLimiter ?? new RateLimiter();
 
@@ -161,7 +160,7 @@ class JobRunner
                     $this->rateLimiter->resetBackoffPause();
                 }
 
-                /** @var AuthenticationDataTrackerInterface $tracker */
+                /** @var DataTrackerInterface $tracker */
                 foreach ($this->trackers as $tracker) {
                     /** @var Job $job */
                     $tracker->process($job->getPayload());
@@ -313,7 +312,7 @@ class JobRunner
             }
 
             if ($cachedState->getJobRunnerId() !== $this->jobRunnerId) {
-                $message = 'Current job runner ID differs from the ID in the cached state.';
+                $message = 'CurrentDataProvider job runner ID differs from the ID in the cached state.';
                 throw new Exception($message);
             }
 
@@ -489,25 +488,6 @@ class JobRunner
         }
     }
 
-    /**
-     * @throws Exception
-     */
-    protected function resolveTrackers(): array
-    {
-        $trackers = [];
-
-        $configuredTrackerClasses = array_merge(
-            [$this->moduleConfiguration->getDefaultDataTrackerAndProviderClass()],
-            $this->moduleConfiguration->getAdditionalTrackers()
-        );
-
-        foreach ($configuredTrackerClasses as $trackerClass) {
-            $trackers[$trackerClass] = $this->authenticationDataTrackerBuilder->build($trackerClass);
-        }
-
-        return $trackers;
-    }
-
     protected function isCli(): bool
     {
         return $this->helpersManager->getEnvironment()->isCli();
diff --git a/src/Services/TrackerResolver.php b/src/Services/TrackerResolver.php
new file mode 100644
index 0000000..575cbf6
--- /dev/null
+++ b/src/Services/TrackerResolver.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace SimpleSAML\Module\accounting\Services;
+
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Providers\Builders\DataProviderBuilder;
+use SimpleSAML\Module\accounting\Data\Trackers\Builders\DataTrackerBuilder;
+use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface;
+use SimpleSAML\Module\accounting\Exceptions\Exception;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+
+class TrackerResolver
+{
+    protected ModuleConfiguration $moduleConfiguration;
+    protected LoggerInterface $logger;
+    protected HelpersManager $helpersManager;
+    protected DataProviderBuilder $dataProviderBuilder;
+    protected DataTrackerBuilder $dataTrackerBuilder;
+
+    public function __construct(
+        ModuleConfiguration $moduleConfiguration,
+        LoggerInterface $logger,
+        HelpersManager $helpersManager,
+        DataProviderBuilder $dataProviderBuilder = null,
+        DataTrackerBuilder $dataTrackerBuilder = null
+    ) {
+        $this->moduleConfiguration = $moduleConfiguration;
+        $this->logger = $logger;
+        $this->helpersManager = $helpersManager;
+        $this->dataProviderBuilder = $dataProviderBuilder ?? new DataProviderBuilder(
+            $this->moduleConfiguration,
+            $this->logger,
+            $this->helpersManager
+        );
+        $this->dataTrackerBuilder = $dataTrackerBuilder ?? new DataTrackerBuilder(
+            $this->moduleConfiguration,
+            $this->logger,
+            $this->helpersManager
+        );
+    }
+
+    /**
+     * @return array<string,DataTrackerInterface>
+     * @throws Exception
+     */
+    public function fromModuleConfiguration(): array
+    {
+        $trackers = [];
+
+        foreach ($this->moduleConfiguration->getProviderClasses() as $providerClass) {
+            if (
+                ($provider = $this->dataProviderBuilder->build($providerClass)) &&
+                ($providersTracker = $provider->getTracker()) !== null
+            ) {
+                $trackers[get_class($providersTracker)] = $providersTracker;
+            }
+        }
+
+        // Process specifically configured trackers.
+        foreach ($this->moduleConfiguration->getAdditionalTrackers() as $trackerClass) {
+            $trackers[$trackerClass] = $this->dataTrackerBuilder->build($trackerClass);
+        }
+
+        return $trackers;
+    }
+}
diff --git a/src/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTable.php b/src/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTable.php
deleted file mode 100644
index 9aa852d..0000000
--- a/src/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTable.php
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations;
-
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
-
-class Version20220601000100CreateJobFailedTable extends Store\Migrations\Bases\AbstractCreateJobsTable
-{
-    protected function getJobsTableName(): string
-    {
-        return 'job_failed';
-    }
-}
diff --git a/src/Trackers/Authentication/DoctrineDbal/Versioned/Tracker.php b/src/Trackers/Authentication/DoctrineDbal/Versioned/Tracker.php
deleted file mode 100644
index 118ae20..0000000
--- a/src/Trackers/Authentication/DoctrineDbal/Versioned/Tracker.php
+++ /dev/null
@@ -1,104 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace SimpleSAML\Module\accounting\Trackers\Authentication\DoctrineDbal\Versioned;
-
-use DateInterval;
-use DateTimeImmutable;
-use Psr\Log\LoggerInterface;
-use SimpleSAML\Module\accounting\Entities\Activity;
-use SimpleSAML\Module\accounting\Entities\Authentication\Event;
-use SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider;
-use SimpleSAML\Module\accounting\Exceptions\InvalidConfigurationException;
-use SimpleSAML\Module\accounting\Exceptions\StoreException;
-use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Providers\Interfaces\AuthenticationDataProviderInterface;
-use SimpleSAML\Module\accounting\Services\HelpersManager;
-use SimpleSAML\Module\accounting\Stores\Builders\DataStoreBuilder;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
-use SimpleSAML\Module\accounting\Stores\Interfaces\DataStoreInterface;
-use SimpleSAML\Module\accounting\Trackers\Interfaces\AuthenticationDataTrackerInterface;
-
-class Tracker implements AuthenticationDataTrackerInterface, AuthenticationDataProviderInterface
-{
-    protected ModuleConfiguration $moduleConfiguration;
-    protected LoggerInterface $logger;
-    protected DataStoreInterface $dataStore;
-    protected HelpersManager $helpersManager;
-
-    /**
-     * @throws StoreException
-     */
-    public function __construct(
-        ModuleConfiguration $moduleConfiguration,
-        LoggerInterface $logger,
-        string $connectionType = ModuleConfiguration\ConnectionType::MASTER,
-        HelpersManager $helpersManager = null,
-        DataStoreInterface $dataStore = null
-    ) {
-        $this->moduleConfiguration = $moduleConfiguration;
-        $this->logger = $logger;
-
-        $this->helpersManager = $helpersManager ?? new HelpersManager();
-
-        // Use provided store or initialize default store for this tracker.
-        $this->dataStore = $dataStore ??
-            (new DataStoreBuilder($this->moduleConfiguration, $this->logger, $this->helpersManager))
-                ->build(
-                    Store::class,
-                    $this->moduleConfiguration->getClassConnectionKey(self::class),
-                    $connectionType
-                );
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public static function build(
-        ModuleConfiguration $moduleConfiguration,
-        LoggerInterface $logger,
-        string $connectionType = ModuleConfiguration\ConnectionType::MASTER
-    ): self {
-        return new self($moduleConfiguration, $logger, $connectionType);
-    }
-
-    public function process(Event $authenticationEvent): void
-    {
-        $this->dataStore->persist($authenticationEvent);
-    }
-
-    public function needsSetup(): bool
-    {
-        return $this->dataStore->needsSetup();
-    }
-
-    public function runSetup(): void
-    {
-        if (! $this->needsSetup()) {
-            $this->logger->warning('Run setup called, however setup is not needed.');
-            return;
-        }
-
-        $this->dataStore->runSetup();
-    }
-
-    public function getConnectedServiceProviders(string $userIdentifier): ConnectedServiceProvider\Bag
-    {
-        $userIdentifierHashSha256 = $this->helpersManager->getHash()->getSha256($userIdentifier);
-        return $this->dataStore->getConnectedOrganizations($userIdentifierHashSha256);
-    }
-
-    public function getActivity(string $userIdentifier, int $maxResults, int $firstResult): Activity\Bag
-    {
-        $userIdentifierHashSha256 = $this->helpersManager->getHash()->getSha256($userIdentifier);
-        return $this->dataStore->getActivity($userIdentifierHashSha256, $maxResults, $firstResult);
-    }
-
-    public function enforceDataRetentionPolicy(DateInterval $retentionPolicy): void
-    {
-        $dateTime = (new DateTimeImmutable())->sub($retentionPolicy);
-
-        $this->dataStore->deleteDataOlderThan($dateTime);
-    }
-}
diff --git a/templates/admin/configuration/status.twig b/templates/admin/configuration/status.twig
index 9927f73..d8b00d9 100644
--- a/templates/admin/configuration/status.twig
+++ b/templates/admin/configuration/status.twig
@@ -10,24 +10,40 @@
     <h2>{{ pagetitle }} </h2>
 
     {% if configurationValidationErrors is not null %}
+
+        <p>There wre some errors. Check your configuration and try again.</p>
+
         <p>{{ configurationValidationErrors }}</p>
+
+        <br>
+
+        {% if runSetup %}
+            <a class="pure-button pure-button-warning" href="?runSetup=1">{{ 'Try again'|trans }}</a>
+        {% endif %}
     {% elseif moduleConfiguration is not null %}
         <ul>
             <li>
                 <strong>{{ 'User ID Attribute Name'|trans }}</strong>: {{ moduleConfiguration.getUserIdAttributeName }}
             </li>
+
             <li>
                 <strong>{{ 'Accounting Processing Type'|trans }}</strong>:
                 {{ moduleConfiguration.getAccountingProcessingType }}
             </li>
-            <li>
-                <strong>{{ 'Default Data Tracker and Provider Class'|trans }}</strong>:
-                {{ moduleConfiguration.getDefaultDataTrackerAndProviderClass }}
-            </li>
-            <li>
-                <strong>{{ 'Tracker and Provider Setup Needed'|trans }}</strong>:
-                {{ defaultDataTrackerAndProvider.needsSetup ? 'Yes'|trans : 'No'|trans }}
-            </li>
+
+            {% if providers is not empty %}
+                <li>
+                    <strong>{{ 'Providers setup'|trans }}</strong>:
+                    <ul>
+                        {% for providerClass, providerInstance in providers %}
+                            <li>
+                                {{ providerClass }}: {{ providerInstance.needsSetup ? 'Yes'|trans : 'No'|trans }}
+                            </li>
+                        {% endfor %}
+                    </ul>
+                </li>
+            {% endif %}
+
             {% if additionalTrackers is not empty %}
                 <li>
                     <strong>{{ 'Additional Trackers and setup'|trans }}</strong>:
@@ -57,21 +73,20 @@
                 </li>
             </ul>
         {% endif %}
-    {% else %}
-        <p>{{ 'Could not initialize module configuration.'|trans }}</p>
-    {% endif %}
 
-    <br>
-
-    {% if setupNeeded %}
-        <p>{{ 'Run setup before using the module.'|trans }}</p>
-        <a class="pure-button pure-button-warning" href="?runSetup=1">{{ 'Run Setup'|trans }}</a>
+        {% if setupNeeded %}
+            <p>{{ 'Run setup before using the module.'|trans }}</p>
+            <a class="pure-button pure-button-warning" href="?runSetup=1">{{ 'Run Setup'|trans }}</a>
+        {% else %}
+            <p>
+                {{ 'Everything seems good to go.'|trans }}
+                <br>
+                <br>
+                {{ ' Profile page URL is'|trans }}: <a href="{{ profilePageUri }}">{{ profilePageUri }}</a>
+            </p>
+        {% endif %}
     {% else %}
-        <p>
-            {{ 'Everything seems good to go.'|trans }}
-            <br>
-            <br>
-            {{ ' Profile page URL is'|trans }}: <a href="{{ profilePageUri }}">{{ profilePageUri }}</a>
-        </p>
+        <p>{{ 'Could not initialize module configuration. Check your settings and try again.'|trans }}</p>
     {% endif %}
+
 {% endblock %}
diff --git a/templates/user/activity.twig b/templates/user/activity.twig
index 7dae7af..e3f3f82 100644
--- a/templates/user/activity.twig
+++ b/templates/user/activity.twig
@@ -1,5 +1,5 @@
 {# @var activityBag \SimpleSAML\Module\accounting\Entities\Activity\Bag #}
-{# @var connectedServiceProvider \SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider #}
+{# @var connectedServiceProvider \SimpleSAML\Module\accounting\Entities\ConnectedService #}
 
 {% extends "@accounting/base.twig" %}
 
diff --git a/templates/user/connected-organizations.twig b/templates/user/connected-organizations.twig
index 00d5454..6bbbf8a 100644
--- a/templates/user/connected-organizations.twig
+++ b/templates/user/connected-organizations.twig
@@ -1,5 +1,5 @@
-{# @var connectedServiceProviderBag \SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider\Bag #}
-{# @var connectedServiceProvider \SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider #}
+{# @var connectedServiceProviderBag \SimpleSAML\Module\accounting\Entities\ConnectedService\Bag #}
+{# @var connectedServiceProvider \SimpleSAML\Module\accounting\Entities\ConnectedService #}
 {# @var accessTokensByClient array #}
 {# @var refreshTokensByClient array #}
 
diff --git a/templates/user/oidc-tokens.twig b/templates/user/oidc-tokens.twig
deleted file mode 100644
index 0f9c6e9..0000000
--- a/templates/user/oidc-tokens.twig
+++ /dev/null
@@ -1,78 +0,0 @@
-{# @var accessTokensByClient array #}
-{# @var refreshTokensByClient array #}
-{# @var clients array #}
-
-{% extends "@accounting/base.twig" %}
-
-{% set pagetitle = 'OIDC Tokens'|trans %}
-
-{% set pageMenuItem = 'oidc-tokens' %}
-
-{% block content %}
-    <section id="banner">
-        <div>
-            {% trans %}Overview of active OIDC Access / Refresh Tokens by OIDC client.{% endtrans %}
-        </div>
-    </section>
-
-    <table>
-        {% for clientId, clientInfo in clients %}
-            <tr>
-                <td colspan="2">
-                    <strong>{{ clientInfo.0.name }}</strong>
-                    <br>
-                    {{ clientInfo.0.description }}
-                </td>
-            </tr>
-
-            {% if accessTokensByClient is not empty and attribute(accessTokensByClient, clientId) %}
-                <tr>
-                    <td colspan="2">{{ 'Access Tokens'|trans }}</td>
-                </tr>
-                {% for accessToken in attribute(accessTokensByClient, clientId) %}
-                <tr>
-                    <td>
-                        {{ accessToken.id }}<br>
-                        {{ 'Expires at'|trans }} {{ accessToken.expires_at|date }}
-                    </td>
-                    <td>
-                        <form method="post" action="oidc-tokens/revoke">
-                            <input type="hidden" name="csrf-token" value="{{ csrfToken.get }}">
-                            <input type="hidden" name="token-type" value="access">
-                            <input type="hidden" name="token-id" value="{{ accessToken.id }}">
-                            <input type="submit" value="Revoke">
-                        </form>
-                    </td>
-                </tr>
-                {% endfor %}
-            {% endif %}
-
-            {% if refreshTokensByClient is not empty and attribute(refreshTokensByClient, clientId) %}
-                <tr>
-                    <td colspan="2">{{ 'Refresh Tokens'|trans }}</td>
-                </tr>
-                {% for refreshToken in attribute(refreshTokensByClient, clientId) %}
-                    <tr>
-                        <td>
-                            {{ refreshToken.id }}<br>
-                            {{ 'Expires at'|trans }} {{ refreshToken.expires_at|date }}
-                        </td>
-                        <td>
-                            <form method="post" action="oidc-tokens/revoke">
-                                <input type="hidden" name="csrf-token" value="{{ csrfToken.get }}">
-                                <input type="hidden" name="token-type" value="refresh">
-                                <input type="hidden" name="token-id" value="{{ refreshToken.id }}">
-                                <input type="submit" value="Revoke">
-                            </form>
-                        </td>
-                    </tr>
-                {% endfor %}
-            {% endif %}
-        {% else %}
-            <tr>
-                <td colspan="3">{{ 'No data available'|trans }}</td>
-            </tr>
-        {% endfor %}
-        <!-- end of repeating item -->
-    </table>
-{% endblock %}
diff --git a/tests/config-templates/config.php b/tests/config-templates/config.php
index 7cc113e..d9f238c 100644
--- a/tests/config-templates/config.php
+++ b/tests/config-templates/config.php
@@ -906,7 +906,7 @@ $config = [
      *************************************/
 
     /*
-     * Tracker processing filters that will be executed for all IdPs
+     * VersionedDataTracker processing filters that will be executed for all IdPs
      */
     'authproc.idp' => [
         /* Enable the authproc filter below to add URN prefixes to all attributes
@@ -961,7 +961,7 @@ $config = [
     ],
 
     /*
-     * Tracker processing filters that will be executed for all SPs
+     * VersionedDataTracker processing filters that will be executed for all SPs
      */
     'authproc.sp' => [
         /*
diff --git a/tests/config-templates/module_accounting.php b/tests/config-templates/module_accounting.php
index 0ecea7b..014f2e5 100644
--- a/tests/config-templates/module_accounting.php
+++ b/tests/config-templates/module_accounting.php
@@ -2,10 +2,10 @@
 
 declare(strict_types=1);
 
+use SimpleSAML\Module\accounting\Data\Providers;
+use SimpleSAML\Module\accounting\Data\Stores;
+use SimpleSAML\Module\accounting\Data\Trackers;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Providers;
-use SimpleSAML\Module\accounting\Stores;
-use SimpleSAML\Module\accounting\Trackers;
 
 $config = [
 
@@ -18,8 +18,11 @@ $config = [
 
     ModuleConfiguration::OPTION_JOBS_STORE => Stores\Jobs\DoctrineDbal\Store::class,
 
-    ModuleConfiguration::OPTION_DEFAULT_DATA_TRACKER_AND_PROVIDER =>
-        Trackers\Authentication\DoctrineDbal\Versioned\Tracker::class,
+    ModuleConfiguration::OPTION_PROVIDER_FOR_ACTIVITY =>
+        Providers\Activity\DoctrineDbal\VersionedDataProvider::class,
+
+    ModuleConfiguration::OPTION_PROVIDER_FOR_CONNECTED_SERVICES =>
+        Providers\ConnectedServices\DoctrineDbal\VersionedDataProvider::class,
 
     ModuleConfiguration::OPTION_ADDITIONAL_TRACKERS => [
         //
@@ -27,7 +30,13 @@ $config = [
 
     ModuleConfiguration::OPTION_CLASS_TO_CONNECTION_MAP => [
         Stores\Jobs\DoctrineDbal\Store::class => 'doctrine_dbal_pdo_sqlite',
-        Trackers\Authentication\DoctrineDbal\Versioned\Tracker::class => [
+        Providers\Activity\DoctrineDbal\VersionedDataProvider::class => [
+            ModuleConfiguration\ConnectionType::MASTER => 'doctrine_dbal_pdo_sqlite',
+            ModuleConfiguration\ConnectionType::SLAVE => [
+                'doctrine_dbal_pdo_sqlite_slave',
+            ],
+        ],
+        Providers\ConnectedServices\DoctrineDbal\VersionedDataProvider::class => [
             ModuleConfiguration\ConnectionType::MASTER => 'doctrine_dbal_pdo_sqlite',
             ModuleConfiguration\ConnectionType::SLAVE => [
                 'doctrine_dbal_pdo_sqlite_slave',
diff --git a/tests/src/Auth/Process/AccountingTest.php b/tests/src/Auth/Process/AccountingTest.php
index 0bcf385..2db27ba 100644
--- a/tests/src/Auth/Process/AccountingTest.php
+++ b/tests/src/Auth/Process/AccountingTest.php
@@ -6,17 +6,18 @@ namespace SimpleSAML\Test\Module\accounting\Auth\Process;
 
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 use SimpleSAML\Module\accounting\Auth\Process\Accounting;
-use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Providers\Activity\DoctrineDbal\VersionedDataProvider;
+use SimpleSAML\Module\accounting\Data\Stores\Builders\JobsStoreBuilder;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store;
+use SimpleSAML\Module\accounting\Data\Trackers\Activity\DoctrineDbal\VersionedDataTracker;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event;
 use SimpleSAML\Module\accounting\Exceptions\InvalidConfigurationException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\HelpersManager;
-use SimpleSAML\Module\accounting\Stores\Builders\JobsStoreBuilder;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
-use SimpleSAML\Module\accounting\Trackers\Authentication\DoctrineDbal\Versioned\Tracker;
-use SimpleSAML\Module\accounting\Trackers\Builders\AuthenticationDataTrackerBuilder;
+use SimpleSAML\Module\accounting\Services\TrackerResolver;
 use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
 
 /**
@@ -25,14 +26,16 @@ use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
  * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractState
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event\State\Saml2
  * @uses \SimpleSAML\Module\accounting\Helpers\AuthenticationEventStateResolver
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator
- * @uses \SimpleSAML\Module\accounting\Stores\Builders\Bases\AbstractStoreBuilder
- * @uses \SimpleSAML\Module\accounting\Trackers\Builders\AuthenticationDataTrackerBuilder
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Builders\Bases\AbstractStoreBuilder
+ * @uses \SimpleSAML\Module\accounting\Data\Trackers\Builders\DataTrackerBuilder
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event\Job
  * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractJob
  * @uses \SimpleSAML\Module\accounting\Helpers\Network
  * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
+ * @uses \SimpleSAML\Module\accounting\Data\Providers\Builders\DataProviderBuilder
+ * @uses \SimpleSAML\Module\accounting\Services\TrackerResolver
  */
 class AccountingTest extends TestCase
 {
@@ -40,11 +43,14 @@ class AccountingTest extends TestCase
     protected MockObject $loggerMock;
     protected array $filterConfig;
     protected MockObject $jobsStoreBuilderMock;
-    protected MockObject $authenticationDataTrackerBuilderMock;
     protected MockObject $jobsStoreMock;
     protected MockObject $trackerMock;
     protected array $sampleState;
     protected HelpersManager $helpersManager;
+    /**
+     * @var MockObject
+     */
+    protected $trackerResolver;
 
     protected function setUp(): void
     {
@@ -53,17 +59,16 @@ class AccountingTest extends TestCase
         $this->loggerMock = $this->createMock(LoggerInterface::class);
 
         $this->jobsStoreBuilderMock = $this->createMock(JobsStoreBuilder::class);
-        $this->authenticationDataTrackerBuilderMock =
-            $this->createMock(AuthenticationDataTrackerBuilder::class);
 
         $this->jobsStoreMock = $this->createMock(Store::class);
-        $this->trackerMock = $this->createMock(Tracker::class);
+        $this->trackerMock = $this->createMock(VersionedDataTracker::class);
 
         $this->sampleState = StateArrays::SAML2_FULL;
 
         $this->filterConfig = [];
 
         $this->helpersManager = new HelpersManager();
+        $this->trackerResolver = $this->createMock(TrackerResolver::class);
     }
 
     public function testCanCreateInstance(): void
@@ -87,7 +92,7 @@ class AccountingTest extends TestCase
                 $this->loggerMock,
                 $this->helpersManager,
                 $this->jobsStoreBuilderMock,
-                $this->authenticationDataTrackerBuilderMock
+                $this->trackerResolver
             )
         );
     }
@@ -108,9 +113,9 @@ class AccountingTest extends TestCase
             ->with($this->equalTo(Store::class))
             ->willReturn($this->jobsStoreMock);
 
-        $this->authenticationDataTrackerBuilderMock
+        $this->trackerResolver
             ->expects($this->never())
-            ->method('build');
+            ->method('fromModuleConfiguration');
 
         (new Accounting(
             $this->filterConfig,
@@ -119,7 +124,7 @@ class AccountingTest extends TestCase
             $this->loggerMock,
             $this->helpersManager,
             $this->jobsStoreBuilderMock,
-            $this->authenticationDataTrackerBuilderMock
+            $this->trackerResolver
         ))->process($this->sampleState);
     }
 
@@ -128,8 +133,8 @@ class AccountingTest extends TestCase
         $this->moduleConfigurationStub->method('getAccountingProcessingType')
             ->willReturn(ModuleConfiguration\AccountingProcessingType::VALUE_SYNCHRONOUS);
 
-        $this->moduleConfigurationStub->method('getDefaultDataTrackerAndProviderClass')
-            ->willReturn(Tracker::class);
+        $this->moduleConfigurationStub->method('getProviderClasses')
+            ->willReturn([VersionedDataProvider::class]);
         $this->moduleConfigurationStub->method('getAdditionalTrackers')->willReturn([]);
 
         $this->jobsStoreBuilderMock->expects($this->never())
@@ -140,11 +145,10 @@ class AccountingTest extends TestCase
             ->method('process')
             ->with($this->isInstanceOf(Event::class));
 
-        $this->authenticationDataTrackerBuilderMock
+        $this->trackerResolver
             ->expects($this->once())
-            ->method('build')
-            ->with($this->equalTo(Tracker::class))
-            ->willReturn($this->trackerMock);
+            ->method('fromModuleConfiguration')
+            ->willReturn([$this->trackerMock]);
 
         (new Accounting(
             $this->filterConfig,
@@ -153,7 +157,7 @@ class AccountingTest extends TestCase
             $this->loggerMock,
             $this->helpersManager,
             $this->jobsStoreBuilderMock,
-            $this->authenticationDataTrackerBuilderMock
+            $this->trackerResolver
         ))->process($this->sampleState);
     }
 
@@ -171,7 +175,7 @@ class AccountingTest extends TestCase
             $this->loggerMock,
             $this->helpersManager,
             $this->jobsStoreBuilderMock,
-            $this->authenticationDataTrackerBuilderMock
+            $this->trackerResolver
         ))->process($this->sampleState);
     }
 }
diff --git a/tests/src/Constants/RawRowResult.php b/tests/src/Constants/RawRowResult.php
index 2d9f044..faa0e7e 100644
--- a/tests/src/Constants/RawRowResult.php
+++ b/tests/src/Constants/RawRowResult.php
@@ -5,24 +5,25 @@ declare(strict_types=1);
 
 namespace SimpleSAML\Test\Module\accounting\Constants;
 
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\TableConstants as ActivityTableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\TableConstants as ConnectedServicesTableConstants;
 
 class RawRowResult
 {
-    public const CONNECTED_ORGANIZATION = [
-        TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS => 1,
-        TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_LAST_AUTHENTICATION_AT => '2022-02-22 22:22:22',
-        TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_FIRST_AUTHENTICATION_AT => '2022-02-02 22:22:22',
-        TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_SP_METADATA => 'a:9:{s:19:"SingleLogoutService";a:1:{i:0;a:2:{s:7:"Binding";s:50:"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect";s:8:"Location";s:121:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/singleLogoutService/default-sp";}}s:24:"AssertionConsumerService";a:2:{i:0;a:3:{s:7:"Binding";s:46:"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST";s:8:"Location";s:126:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/assertionConsumerService/default-sp";s:5:"index";i:0;}i:1;a:3:{s:7:"Binding";s:50:"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact";s:8:"Location";s:126:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/assertionConsumerService/default-sp";s:5:"index";i:1;}}s:8:"contacts";a:1:{i:0;a:3:{s:12:"emailAddress";s:15:"mivanci@srce.hr";s:9:"givenName";s:15:"Marko Ivančić";s:11:"contactType";s:9:"technical";}}s:4:"name";a:1:{s:2:"en";s:12:"Test service";}s:11:"description";a:1:{s:2:"en";s:27:"Description of test service";}s:16:"OrganizationName";a:1:{s:2:"en";s:17:"Test organization";}s:8:"entityid";s:114:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/metadata.php/default-sp";s:14:"metadata-index";s:114:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/metadata.php/default-sp";s:12:"metadata-set";s:16:"saml20-sp-remote";}',
-        TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_USER_ATTRIBUTES => 'a:5:{s:33:"urn:oid:0.9.2342.19200300.100.1.1";a:1:{i:0;s:11:"student-uid";}s:32:"urn:oid:1.3.6.1.4.1.5923.1.1.1.1";a:2:{i:0;s:6:"member";i:1;s:7:"student";}s:15:"urn:oid:2.5.4.4";a:1:{i:0;s:10:"student-sn";}s:19:"hrEduPersonUniqueID";a:1:{i:0;s:19:"student@example.org";}s:23:"hrEduPersonPersistentID";a:1:{i:0;s:13:"student123abc";}}',
+    public const CONNECTED_SERVICE = [
+        ConnectedServicesTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS => 1,
+        ConnectedServicesTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT => '2022-02-22 22:22:22',
+        ConnectedServicesTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT => '2022-02-02 22:22:22',
+        ConnectedServicesTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA => 'a:9:{s:19:"SingleLogoutService";a:1:{i:0;a:2:{s:7:"Binding";s:50:"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect";s:8:"Location";s:121:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/singleLogoutService/default-sp";}}s:24:"AssertionConsumerService";a:2:{i:0;a:3:{s:7:"Binding";s:46:"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST";s:8:"Location";s:126:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/assertionConsumerService/default-sp";s:5:"index";i:0;}i:1;a:3:{s:7:"Binding";s:50:"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact";s:8:"Location";s:126:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/assertionConsumerService/default-sp";s:5:"index";i:1;}}s:8:"contacts";a:1:{i:0;a:3:{s:12:"emailAddress";s:15:"mivanci@srce.hr";s:9:"givenName";s:15:"Marko Ivančić";s:11:"contactType";s:9:"technical";}}s:4:"name";a:1:{s:2:"en";s:12:"Test service";}s:11:"description";a:1:{s:2:"en";s:27:"Description of test service";}s:16:"OrganizationName";a:1:{s:2:"en";s:17:"Test organization";}s:8:"entityid";s:114:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/metadata.php/default-sp";s:14:"metadata-index";s:114:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/metadata.php/default-sp";s:12:"metadata-set";s:16:"saml20-sp-remote";}',
+        ConnectedServicesTableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES => 'a:5:{s:33:"urn:oid:0.9.2342.19200300.100.1.1";a:1:{i:0;s:11:"student-uid";}s:32:"urn:oid:1.3.6.1.4.1.5923.1.1.1.1";a:2:{i:0;s:6:"member";i:1;s:7:"student";}s:15:"urn:oid:2.5.4.4";a:1:{i:0;s:10:"student-sn";}s:19:"hrEduPersonUniqueID";a:1:{i:0;s:19:"student@example.org";}s:23:"hrEduPersonPersistentID";a:1:{i:0;s:13:"student123abc";}}',
     ];
 
 
     public const ACTIVITY = [
-        TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT => '2022-02-22 22:22:22',
-        TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA => 'a:9:{s:19:"SingleLogoutService";a:1:{i:0;a:2:{s:7:"Binding";s:50:"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect";s:8:"Location";s:121:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/singleLogoutService/default-sp";}}s:24:"AssertionConsumerService";a:2:{i:0;a:3:{s:7:"Binding";s:46:"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST";s:8:"Location";s:126:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/assertionConsumerService/default-sp";s:5:"index";i:0;}i:1;a:3:{s:7:"Binding";s:50:"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact";s:8:"Location";s:126:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/assertionConsumerService/default-sp";s:5:"index";i:1;}}s:8:"contacts";a:1:{i:0;a:3:{s:12:"emailAddress";s:15:"mivanci@srce.hr";s:9:"givenName";s:15:"Marko Ivančić";s:11:"contactType";s:9:"technical";}}s:4:"name";a:1:{s:2:"en";s:12:"Test service";}s:11:"description";a:1:{s:2:"en";s:27:"Description of test service";}s:16:"OrganizationName";a:1:{s:2:"en";s:17:"Test organization";}s:8:"entityid";s:114:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/metadata.php/default-sp";s:14:"metadata-index";s:114:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/metadata.php/default-sp";s:12:"metadata-set";s:16:"saml20-sp-remote";}',
-        TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES => 'a:5:{s:33:"urn:oid:0.9.2342.19200300.100.1.1";a:1:{i:0;s:11:"student-uid";}s:32:"urn:oid:1.3.6.1.4.1.5923.1.1.1.1";a:2:{i:0;s:6:"member";i:1;s:7:"student";}s:15:"urn:oid:2.5.4.4";a:1:{i:0;s:10:"student-sn";}s:19:"hrEduPersonUniqueID";a:1:{i:0;s:19:"student@example.org";}s:23:"hrEduPersonPersistentID";a:1:{i:0;s:13:"student123abc";}}',
-        TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS => '172.21.0.1',
-        TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION => 'SAML2',
+        ActivityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT => '2022-02-22 22:22:22',
+        ActivityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_SP_METADATA => 'a:9:{s:19:"SingleLogoutService";a:1:{i:0;a:2:{s:7:"Binding";s:50:"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect";s:8:"Location";s:121:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/singleLogoutService/default-sp";}}s:24:"AssertionConsumerService";a:2:{i:0;a:3:{s:7:"Binding";s:46:"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST";s:8:"Location";s:126:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/assertionConsumerService/default-sp";s:5:"index";i:0;}i:1;a:3:{s:7:"Binding";s:50:"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact";s:8:"Location";s:126:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/assertionConsumerService/default-sp";s:5:"index";i:1;}}s:8:"contacts";a:1:{i:0;a:3:{s:12:"emailAddress";s:15:"mivanci@srce.hr";s:9:"givenName";s:15:"Marko Ivančić";s:11:"contactType";s:9:"technical";}}s:4:"name";a:1:{s:2:"en";s:12:"Test service";}s:11:"description";a:1:{s:2:"en";s:27:"Description of test service";}s:16:"OrganizationName";a:1:{s:2:"en";s:17:"Test organization";}s:8:"entityid";s:114:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/metadata.php/default-sp";s:14:"metadata-index";s:114:"https://pc-mivancic.srce.hr:9074/simplesamlphp/simplesamlphp-2-beta-git/module.php/saml/sp/metadata.php/default-sp";s:12:"metadata-set";s:16:"saml20-sp-remote";}',
+        ActivityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_USER_ATTRIBUTES => 'a:5:{s:33:"urn:oid:0.9.2342.19200300.100.1.1";a:1:{i:0;s:11:"student-uid";}s:32:"urn:oid:1.3.6.1.4.1.5923.1.1.1.1";a:2:{i:0;s:6:"member";i:1;s:7:"student";}s:15:"urn:oid:2.5.4.4";a:1:{i:0;s:10:"student-sn";}s:19:"hrEduPersonUniqueID";a:1:{i:0;s:19:"student@example.org";}s:23:"hrEduPersonPersistentID";a:1:{i:0;s:13:"student123abc";}}',
+        ActivityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_CLIENT_IP_ADDRESS => '172.21.0.1',
+        ActivityTableConstants::ENTITY_ACTIVITY_COLUMN_NAME_AUTHENTICATION_PROTOCOL_DESIGNATION => 'SAML2',
     ];
 }
diff --git a/tests/src/Data/Providers/Builders/DataProviderBuilderTest.php b/tests/src/Data/Providers/Builders/DataProviderBuilderTest.php
new file mode 100644
index 0000000..7b2b5ac
--- /dev/null
+++ b/tests/src/Data/Providers/Builders/DataProviderBuilderTest.php
@@ -0,0 +1,90 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Test\Module\accounting\Data\Providers\Builders;
+
+use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Providers\Builders\DataProviderBuilder;
+use SimpleSAML\Module\accounting\Data\Trackers\Activity\DoctrineDbal\VersionedDataTracker;
+use SimpleSAML\Module\accounting\Exceptions\Exception;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
+use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
+
+/**
+ * @covers \SimpleSAML\Module\accounting\Data\Providers\Builders\DataProviderBuilder
+ * @uses \SimpleSAML\Module\accounting\Helpers\InstanceBuilderUsingModuleConfiguration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Builders\Bases\AbstractStoreBuilder
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Builders\DataStoreBuilder
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store
+ * @uses \SimpleSAML\Module\accounting\Data\Trackers\Activity\DoctrineDbal\VersionedDataTracker
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Providers\Activity\DoctrineDbal\VersionedDataProvider
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository
+ */
+class DataProviderBuilderTest extends TestCase
+{
+    protected Stub $moduleConfigurationStub;
+
+    protected Stub $loggerStub;
+    protected HelpersManager $helpersManager;
+
+    protected function setUp(): void
+    {
+        $this->moduleConfigurationStub = $this->createStub(ModuleConfiguration::class);
+        $connectionParams = ConnectionParameters::DBAL_SQLITE_MEMORY;
+        $this->moduleConfigurationStub->method('getConnectionParameters')
+            ->willReturn($connectionParams);
+
+        $this->loggerStub = $this->createStub(LoggerInterface::class);
+        $this->helpersManager = new HelpersManager();
+    }
+
+    public function testCanCreateInstance(): void
+    {
+        $this->assertInstanceOf(
+            DataProviderBuilder::class,
+            new DataProviderBuilder(
+                $this->moduleConfigurationStub,
+                $this->loggerStub,
+                $this->helpersManager
+            )
+        );
+    }
+
+    /**
+     * @throws Exception
+     */
+    public function testCanBuildDataProvider(): void
+    {
+        $builder = new DataProviderBuilder(
+            $this->moduleConfigurationStub,
+            $this->loggerStub,
+            $this->helpersManager
+        );
+
+        $this->assertInstanceOf(VersionedDataTracker::class, $builder->build(VersionedDataTracker::class));
+    }
+
+    public function testThrowsForInvalidClass(): void
+    {
+        $this->expectException(Exception::class);
+
+        (new DataProviderBuilder(
+            $this->moduleConfigurationStub,
+            $this->loggerStub,
+            $this->helpersManager
+        ))->build('invalid');
+    }
+}
diff --git a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTableTest.php b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTableTest.php
similarity index 84%
rename from tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTableTest.php
rename to tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTableTest.php
index d309767..c1a790d 100644
--- a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTableTest.php
+++ b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/Migrations/Version20220801000700CreateAuthenticationEventTableTest.php
@@ -2,22 +2,22 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations;
 
 use Doctrine\DBAL\Exception;
 use Doctrine\DBAL\Schema\AbstractSchemaManager;
 use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use PHPUnit\Framework\TestCase;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000700CreateAuthenticationEventTable
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection *
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000700CreateAuthenticationEventTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
  */
 class Version20220801000700CreateAuthenticationEventTableTest extends TestCase
 {
diff --git a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawActivityTest.php b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RawActivityTest.php
similarity index 89%
rename from tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawActivityTest.php
rename to tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RawActivityTest.php
index 3158985..73f3322 100644
--- a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawActivityTest.php
+++ b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RawActivityTest.php
@@ -2,21 +2,21 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store;
 
 use DateTimeImmutable;
 use Doctrine\DBAL\Platforms\AbstractPlatform;
 use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\RawActivity;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\TableConstants;
 use SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2;
 use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\RawActivity;
-use PHPUnit\Framework\TestCase;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\TableConstants;
 use SimpleSAML\Test\Module\accounting\Constants\DateTime;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\RawActivity
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractRawEntity
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\RawActivity
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity
  */
 class RawActivityTest extends TestCase
 {
@@ -62,7 +62,10 @@ class RawActivityTest extends TestCase
     {
         $rawActivity = new RawActivity($this->rawRow, $this->abstractPlatformStub);
 
-        $this->assertInstanceOf(RawActivity::class, $rawActivity);
+        $this->assertInstanceOf(
+            RawActivity::class,
+            $rawActivity
+        );
     }
 
     public function testCanGetProperties(): void
diff --git a/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RepositoryTest.php b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RepositoryTest.php
new file mode 100644
index 0000000..fbf3b36
--- /dev/null
+++ b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/Store/RepositoryTest.php
@@ -0,0 +1,328 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store;
+
+use DateInterval;
+use DateTimeImmutable;
+use Exception;
+use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Repository;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants
+    as BaseTableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator;
+use SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
+use SimpleSAML\Test\Module\accounting\Constants\DateTime;
+
+/**
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Helpers\Filesystem
+ * @uses \SimpleSAML\Module\accounting\ModuleConfiguration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000000CreateIdpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000100CreateIdpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000200CreateSpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000300CreateSpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000400CreateUserTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000500CreateUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000600CreateIdpSpUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000700CreateAuthenticationEventTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateSpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateSpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpSpUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
+ */
+class RepositoryTest extends TestCase
+{
+    protected Connection $connection;
+    protected \Doctrine\DBAL\Connection $dbal;
+    /**
+     * @var Stub
+     */
+    protected $loggerStub;
+    protected Migrator $migrator;
+    protected string $dateTimeFormat;
+    protected string $idpEntityId;
+    protected string $idpEntityIdHash;
+    protected string $idpMetadata;
+    protected string $idpMetadataHash;
+    protected string $spEntityId;
+    protected string $spMetadataHash;
+    protected string $userIdentifier;
+    protected string $userIdentifierHash;
+    protected string $userAttributes;
+    protected string $userAttributesHash;
+    protected Repository $repository;
+    protected DateTimeImmutable $createdAt;
+    /**
+     * @var Stub
+     */
+    protected $connectionStub;
+    protected string $spEntityIdHash;
+    protected string $spMetadata;
+    protected string $clientIpAddress;
+    protected string $authenticationProtocolDesignation;
+
+    /**
+     * @throws StoreException
+     * @throws MigrationException
+     */
+    protected function setUp(): void
+    {
+        // For stubbing.
+        $this->connectionStub = $this->createStub(Connection::class);
+        $this->loggerStub = $this->createStub(LoggerInterface::class);
+
+        // For real DB testing.
+        $connectionParameters = ConnectionParameters::DBAL_SQLITE_MEMORY;
+        $this->connection = new Connection($connectionParameters);
+        $this->migrator = new Migrator($this->connection, $this->loggerStub);
+        $moduleConfiguration = new ModuleConfiguration();
+        $migrationsDirectory = $moduleConfiguration->getModuleSourceDirectory() . DIRECTORY_SEPARATOR .
+            'Data' . DIRECTORY_SEPARATOR .
+            'Stores' . DIRECTORY_SEPARATOR .
+            'Accounting' . DIRECTORY_SEPARATOR .
+            'Activity' . DIRECTORY_SEPARATOR .
+            'DoctrineDbal' . DIRECTORY_SEPARATOR .
+            'Versioned' . DIRECTORY_SEPARATOR .
+            'Store' . DIRECTORY_SEPARATOR .
+            AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME;
+        $namespace = Store::class . '\\' . AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME;
+
+        $this->migrator->runSetup();
+        $this->migrator->runNonImplementedMigrationClasses($migrationsDirectory, $namespace);
+
+        $this->repository = new Repository($this->connection, $this->loggerStub);
+
+        $this->dateTimeFormat = DateTime::DEFAULT_FORMAT;
+
+        $this->idpEntityId = 'idp-entity-id';
+        $this->idpEntityIdHash = 'idp-entity-id-hash';
+
+        $this->idpMetadata = 'idp-metadata';
+        $this->idpMetadataHash = 'idp-metadata-hash';
+
+        $this->spEntityId = 'sp-entity-id';
+        $this->spEntityIdHash = 'sp-entity-id-hash';
+
+        $this->spMetadata = 'sp-metadata';
+        $this->spMetadataHash = 'sp-metadata-hash';
+
+        $this->userIdentifier = 'user-identifier';
+        $this->userIdentifierHash = 'user-identifier-hash';
+
+        $this->userAttributes = 'user-attributes';
+        $this->userAttributesHash = 'user-attributes-hash';
+
+        $this->createdAt = new DateTimeImmutable();
+        $this->clientIpAddress = '123.123.123.123';
+        $this->authenticationProtocolDesignation = Saml2::DESIGNATION;
+    }
+
+    public function testCanCreateInstance(): void
+    {
+        $this->assertInstanceOf(
+            Repository::class,
+            new Repository($this->connection, $this->loggerStub)
+        );
+    }
+
+    public function testInsertAuthenticationEventThrowsOnInvalidDbal(): void
+    {
+        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
+        $repository = new Repository($this->connectionStub, $this->loggerStub);
+        $this->expectException(StoreException::class);
+
+        $repository->insertAuthenticationEvent(1, $this->createdAt);
+    }
+
+    /**
+     * @throws StoreException
+     * @throws \Doctrine\DBAL\Exception
+     */
+    public function testCanGetActivity(): void
+    {
+        $this->repository->insertIdp($this->idpEntityId, $this->idpEntityIdHash, $this->createdAt);
+        $idpResult = $this->repository->getIdp($this->idpEntityIdHash)->fetchAssociative();
+        $idpId = (int)$idpResult[BaseTableConstants::TABLE_IDP_COLUMN_NAME_ID];
+        $this->repository->insertIdpVersion($idpId, $this->idpMetadata, $this->idpMetadataHash, $this->createdAt);
+        $idpVersionResult = $this->repository->getIdpVersion($idpId, $this->idpMetadataHash)->fetchAssociative();
+
+        $this->repository->insertSp($this->spEntityId, $this->spEntityIdHash, $this->createdAt);
+        $spResult = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative();
+        $spId = (int)$spResult[BaseTableConstants::TABLE_SP_COLUMN_NAME_ID];
+        $this->repository->insertSpVersion($spId, $this->spMetadata, $this->spMetadataHash, $this->createdAt);
+        $spVersionResult = $this->repository->getSpVersion($spId, $this->spMetadataHash)->fetchAssociative();
+
+        $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt);
+        $userResult = $this->repository->getUser($this->userIdentifierHash)->fetchAssociative();
+        $userId = (int)$userResult[BaseTableConstants::TABLE_USER_COLUMN_NAME_ID];
+        $this->repository
+            ->insertUserVersion($userId, $this->userAttributes, $this->userAttributesHash, $this->createdAt);
+        $userVersionResult = $this->repository->getUserVersion($userId, $this->userAttributesHash)->fetchAssociative();
+
+        $idpVersionId = (int)$idpVersionResult[BaseTableConstants::TABLE_IDP_VERSION_COLUMN_NAME_ID];
+        $spVersionId = (int)$spVersionResult[BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID];
+        $userVersionId = (int)$userVersionResult[BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID];
+
+        $this->repository->insertIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId, $this->createdAt);
+        $idpSpUserVersionResult = $this->repository->getIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId)
+            ->fetchAssociative();
+
+        $idpSpUserVersionId =
+            (int)$idpSpUserVersionResult[BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
+
+        $this->repository->insertAuthenticationEvent(
+            $idpSpUserVersionId,
+            $this->createdAt,
+            $this->clientIpAddress,
+            $this->authenticationProtocolDesignation,
+            $this->createdAt
+        );
+
+        $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0);
+        $this->assertCount(1, $resultArray);
+
+        $this->repository->insertAuthenticationEvent(
+            $idpSpUserVersionId,
+            $this->createdAt,
+            $this->clientIpAddress,
+            $this->authenticationProtocolDesignation,
+            $this->createdAt
+        );
+        $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0);
+        $this->assertCount(2, $resultArray);
+
+        $this->repository->insertAuthenticationEvent(
+            $idpSpUserVersionId,
+            $this->createdAt,
+            $this->clientIpAddress,
+            $this->authenticationProtocolDesignation,
+            $this->createdAt
+        );
+        $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0);
+        $this->assertCount(3, $resultArray);
+
+        // Simulate another SP
+        $spEntityIdNew = $this->spEntityId . '-new';
+        $spEntityIdHashNew = $this->spEntityIdHash . '-new';
+        $spMetadataNew = $this->spMetadata . '-new';
+        $spMetadataHashNew = $this->spMetadataHash . '-new';
+        $this->repository->insertSp($spEntityIdNew, $spEntityIdHashNew, $this->createdAt);
+        $spResult = $this->repository->getSp($spEntityIdHashNew)->fetchAssociative();
+        $spId = (int)$spResult[BaseTableConstants::TABLE_SP_COLUMN_NAME_ID];
+        $this->repository->insertSpVersion($spId, $spMetadataNew, $spMetadataHashNew, $this->createdAt);
+        $spVersionResult = $this->repository->getSpVersion($spId, $spMetadataHashNew)->fetchAssociative();
+        $spVersionId = (int)$spVersionResult[BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID];
+
+        $this->repository->insertIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId, $this->createdAt);
+        $idpSpUserVersionResult = $this->repository->getIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId)
+            ->fetchAssociative();
+
+        $idpSpUserVersionId =
+            (int)$idpSpUserVersionResult[BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
+
+        $this->repository->insertAuthenticationEvent(
+            $idpSpUserVersionId,
+            $this->createdAt,
+            $this->clientIpAddress,
+            $this->authenticationProtocolDesignation,
+            $this->createdAt
+        );
+        $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0);
+        $this->assertCount(4, $resultArray);
+
+        // Simulate a change in user attributes
+    }
+
+    public function testGetActivityThrowsOnInvalidDbal(): void
+    {
+        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
+        $repository = new Repository($this->connectionStub, $this->loggerStub);
+        $this->expectException(StoreException::class);
+
+        $repository->getActivity($this->userIdentifierHash, 10, 0);
+    }
+
+    /**
+     * @throws StoreException
+     * @throws \Doctrine\DBAL\Exception
+     */
+    public function testCanDeleteAuthenticationEventsOlderThan(): void
+    {
+        $this->repository->insertIdp($this->idpEntityId, $this->idpEntityIdHash, $this->createdAt);
+        $idpResult = $this->repository->getIdp($this->idpEntityIdHash)->fetchAssociative();
+        $idpId = (int)$idpResult[BaseTableConstants::TABLE_IDP_COLUMN_NAME_ID];
+        $this->repository->insertIdpVersion($idpId, $this->idpMetadata, $this->idpMetadataHash, $this->createdAt);
+        $idpVersionResult = $this->repository->getIdpVersion($idpId, $this->idpMetadataHash)->fetchAssociative();
+
+        $this->repository->insertSp($this->spEntityId, $this->spEntityIdHash, $this->createdAt);
+        $spResult = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative();
+        $spId = (int)$spResult[BaseTableConstants::TABLE_SP_COLUMN_NAME_ID];
+        $this->repository->insertSpVersion($spId, $this->spMetadata, $this->spMetadataHash, $this->createdAt);
+        $spVersionResult = $this->repository->getSpVersion($spId, $this->spMetadataHash)->fetchAssociative();
+
+        $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt);
+        $userResult = $this->repository->getUser($this->userIdentifierHash)->fetchAssociative();
+        $userId = (int)$userResult[BaseTableConstants::TABLE_USER_COLUMN_NAME_ID];
+        $this->repository
+            ->insertUserVersion($userId, $this->userAttributes, $this->userAttributesHash, $this->createdAt);
+        $userVersionResult = $this->repository->getUserVersion($userId, $this->userAttributesHash)->fetchAssociative();
+
+        $idpVersionId = (int)$idpVersionResult[BaseTableConstants::TABLE_IDP_VERSION_COLUMN_NAME_ID];
+        $spVersionId = (int)$spVersionResult[BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID];
+        $userVersionId = (int)$userVersionResult[BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID];
+
+        $this->repository->insertIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId, $this->createdAt);
+        $idpSpUserVersionResult = $this->repository->getIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId)
+            ->fetchAssociative();
+
+        $idpSpUserVersionId =
+            (int)$idpSpUserVersionResult[BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
+
+        $this->repository->insertAuthenticationEvent(
+            $idpSpUserVersionId,
+            $this->createdAt,
+            $this->clientIpAddress,
+            $this->authenticationProtocolDesignation,
+            $this->createdAt
+        );
+
+        $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0);
+        $this->assertCount(1, $resultArray);
+
+        $dateTimeInFuture = $this->createdAt->add(new DateInterval('P1D'));
+
+        $this->repository->deleteAuthenticationEventsOlderThan($dateTimeInFuture);
+
+        $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0);
+        $this->assertCount(0, $resultArray);
+    }
+
+    public function testDeleteAuthenticationEventsOlderThanThrowsOnInvalidDbal(): void
+    {
+        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
+        $repository = new Repository($this->connectionStub, $this->loggerStub);
+        $this->expectException(StoreException::class);
+
+        $repository->deleteAuthenticationEventsOlderThan(new DateTimeImmutable());
+    }
+}
diff --git a/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/StoreTest.php b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/StoreTest.php
new file mode 100644
index 0000000..d86650e
--- /dev/null
+++ b/tests/src/Data/Stores/Accounting/Activity/DoctrineDbal/Versioned/StoreTest.php
@@ -0,0 +1,416 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned;
+
+use DateTimeImmutable;
+use Doctrine\DBAL\Result;
+use Exception;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants
+    as BaseTableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator;
+use SimpleSAML\Module\accounting\Entities\Authentication\Event;
+use SimpleSAML\Module\accounting\Entities\Authentication\Event\State;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
+use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
+use SimpleSAML\Test\Module\accounting\Constants\RawRowResult;
+use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
+
+/**
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event
+ * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractState
+ * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event\State\Saml2
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000000CreateIdpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000100CreateIdpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000200CreateSpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000300CreateSpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000400CreateUserTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000500CreateUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000600CreateIdpSpUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Migrations\Version20220801000700CreateAuthenticationEventTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateSpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateSpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpSpUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Helpers\Filesystem
+ * @uses \SimpleSAML\Module\accounting\Helpers\Hash
+ * @uses \SimpleSAML\Module\accounting\Helpers\Arr
+ * @uses \SimpleSAML\Module\accounting\Helpers\Network
+ * @uses \SimpleSAML\Module\accounting\Entities\ConnectedService\Bag
+ * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractProvider
+ * @uses \SimpleSAML\Module\accounting\Entities\ConnectedService
+ * @uses \SimpleSAML\Module\accounting\Entities\User
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity
+ * @uses \SimpleSAML\Module\accounting\Entities\Activity\Bag
+ * @uses \SimpleSAML\Module\accounting\Entities\Activity
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\RawActivity
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Entities\Providers\Service\Saml2
+ * @uses \SimpleSAML\Module\accounting\Helpers\ProviderResolver
+ * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2
+ */
+class StoreTest extends TestCase
+{
+    protected Stub $moduleConfigurationStub;
+    protected Migrator $migrator;
+    protected Stub $factoryStub;
+    protected Connection $connection;
+    protected State\Saml2 $state;
+    protected Event $authenticationEvent;
+    protected HashDecoratedState $hashDecoratedState;
+    /**
+     * @var MockObject
+     */
+    protected $repositoryMock;
+    /**
+     * @var Stub
+     */
+    protected $resultStub;
+    /**
+     * @var MockObject
+     */
+    protected $loggerMock;
+    /**
+     * @var MockObject
+     */
+    protected $helpersManagerMock;
+
+    /**
+     * @throws StoreException
+     */
+    protected function setUp(): void
+    {
+        $connectionParams = ConnectionParameters::DBAL_SQLITE_MEMORY;
+        $this->moduleConfigurationStub = $this->createStub(ModuleConfiguration::class);
+        $this->moduleConfigurationStub->method('getConnectionParameters')
+            ->willReturn($connectionParams);
+        $this->moduleConfigurationStub->method('getUserIdAttributeName')
+            ->willReturn('hrEduPersonPersistentID');
+
+        $this->connection = new Connection($connectionParams);
+
+        $this->loggerMock = $this->createMock(LoggerInterface::class);
+
+        $this->migrator = new Migrator($this->connection, $this->loggerMock);
+
+        $this->factoryStub = $this->createStub(Factory::class);
+        $this->factoryStub->method('buildConnection')->willReturn($this->connection);
+        $this->factoryStub->method('buildMigrator')->willReturn($this->migrator);
+
+        $this->state = new State\Saml2(StateArrays::SAML2_FULL);
+        $this->authenticationEvent = new Event($this->state);
+
+        $this->hashDecoratedState = new HashDecoratedState($this->state);
+        $this->repositoryMock = $this->createMock(
+            Store\Repository::class
+        );
+
+        $this->resultStub = $this->createStub(Result::class);
+        $this->helpersManagerMock = $this->createMock(HelpersManager::class);
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testCanConstructInstance(): void
+    {
+        $this->assertInstanceOf(
+            Store::class,
+            new Store(
+                $this->moduleConfigurationStub,
+                $this->loggerMock,
+                null,
+                ModuleConfiguration\ConnectionType::MASTER,
+                $this->factoryStub
+            )
+        );
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testCanBuildInstance(): void
+    {
+        $this->assertInstanceOf(
+            Store::class,
+            Store::build($this->moduleConfigurationStub, $this->loggerMock)
+        );
+    }
+
+    /**
+     * @throws StoreException
+     * @throws \Doctrine\DBAL\Exception
+     * @throws MigrationException
+     */
+    public function testCanPersistAuthenticationEvent(): void
+    {
+        $store = new Store(
+            $this->moduleConfigurationStub,
+            $this->loggerMock,
+            null,
+            ModuleConfiguration\ConnectionType::MASTER,
+            $this->factoryStub
+        );
+        $store->runSetup();
+
+        $idpCountQueryBuilder = $this->connection->dbal()->createQueryBuilder();
+        $idpVersionCountQueryBuilder = $this->connection->dbal()->createQueryBuilder();
+        $spCountQueryBuilder = $this->connection->dbal()->createQueryBuilder();
+        $spVersionCountQueryBuilder = $this->connection->dbal()->createQueryBuilder();
+        $userCountQueryBuilder = $this->connection->dbal()->createQueryBuilder();
+        $userVersionCountQueryBuilder = $this->connection->dbal()->createQueryBuilder();
+        $idpSpUserVersionCountQueryBuilder = $this->connection->dbal()->createQueryBuilder();
+        $authenticationEventCountQueryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+        $idpCountQueryBuilder->select('COUNT(id) as idpCount')->from(
+            //'vds_idp'
+            $this->connection->preparePrefixedTableName(
+                BaseTableConstants::TABLE_PREFIX . BaseTableConstants::TABLE_NAME_IDP
+            )
+        );
+        $idpVersionCountQueryBuilder->select('COUNT(id) as idpVersionCount')->from(
+            //'vds_idp_version'
+            $this->connection->preparePrefixedTableName(
+                BaseTableConstants::TABLE_PREFIX . BaseTableConstants::TABLE_NAME_IDP_VERSION
+            )
+        );
+        $spCountQueryBuilder->select('COUNT(id) as spCount')->from(
+            //'vds_sp'
+            $this->connection->preparePrefixedTableName(
+                BaseTableConstants::TABLE_PREFIX . BaseTableConstants::TABLE_NAME_SP
+            )
+        );
+        $spVersionCountQueryBuilder->select('COUNT(id) as spVersionCount')->from(
+            //'vds_sp_version'
+            $this->connection->preparePrefixedTableName(
+                BaseTableConstants::TABLE_PREFIX . BaseTableConstants::TABLE_NAME_SP_VERSION
+            )
+        );
+        $userCountQueryBuilder->select('COUNT(id) as userCount')->from(
+            //'vds_user'
+            $this->connection->preparePrefixedTableName(
+                BaseTableConstants::TABLE_PREFIX . BaseTableConstants::TABLE_NAME_USER
+            )
+        );
+        $userVersionCountQueryBuilder->select('COUNT(id) as userVersionCount')->from(
+            //'vds_user_version'
+            $this->connection->preparePrefixedTableName(
+                BaseTableConstants::TABLE_PREFIX . BaseTableConstants::TABLE_NAME_USER_VERSION
+            )
+        );
+        $idpSpUserVersionCountQueryBuilder->select('COUNT(id) as idpSpUserVersionCount')
+            ->from(
+                //'vds_idp_sp_user_version'
+                $this->connection->preparePrefixedTableName(
+                    BaseTableConstants::TABLE_PREFIX . BaseTableConstants::TABLE_NAME_IDP_SP_USER_VERSION
+                )
+            );
+        $authenticationEventCountQueryBuilder->select('COUNT(id) as authenticationEventCount')
+            ->from(
+                //'vds_authentication_event'
+                $this->connection->preparePrefixedTableName(
+                    BaseTableConstants::TABLE_PREFIX . TableConstants::TABLE_NAME_AUTHENTICATION_EVENT
+                )
+            );
+
+        $this->assertSame(0, (int)$idpCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(0, (int)$idpVersionCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(0, (int)$spCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(0, (int)$spVersionCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(0, (int)$userCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(0, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(0, (int)$idpSpUserVersionCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(0, (int)$authenticationEventCountQueryBuilder->executeQuery()->fetchOne());
+
+        $store->persist($this->authenticationEvent);
+
+        $this->assertSame(1, (int)$idpCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(1, (int)$idpVersionCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(1, (int)$spCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(1, (int)$spVersionCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(1, (int)$userCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(1, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(1, (int)$idpSpUserVersionCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(1, (int)$authenticationEventCountQueryBuilder->executeQuery()->fetchOne());
+
+        $store->persist($this->authenticationEvent);
+
+        $this->assertSame(1, (int)$idpCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(1, (int)$idpVersionCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(1, (int)$spCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(1, (int)$spVersionCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(1, (int)$userCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(1, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(1, (int)$idpSpUserVersionCountQueryBuilder->executeQuery()->fetchOne());
+        $this->assertSame(2, (int)$authenticationEventCountQueryBuilder->executeQuery()->fetchOne());
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testResolveIdpSpUserVersionIdThrowsOnFirstGetIdpSpUserVersionFailure(): void
+    {
+        $this->repositoryMock->method('getIdpSpUserVersion')->willThrowException(new Exception('test'));
+        $store = new Store(
+            $this->moduleConfigurationStub,
+            $this->loggerMock,
+            null,
+            ModuleConfiguration\ConnectionType::MASTER,
+            $this->factoryStub,
+            $this->helpersManagerMock,
+            $this->repositoryMock
+        );
+
+        $this->expectException(StoreException::class);
+
+        $store->persist($this->authenticationEvent);
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testResolveIdpSpUserVersionIdThrowsOnInsertAndGetIdpSpUserVersionFailure(): void
+    {
+        $this->resultStub->method('fetchOne')->willReturn(false);
+        $this->repositoryMock->method('getIdpSpUserVersion')->willReturn($this->resultStub);
+        $this->repositoryMock->method('insertIdpSpUserVersion')->willThrowException(new Exception('test'));
+
+        $store = new Store(
+            $this->moduleConfigurationStub,
+            $this->loggerMock,
+            null,
+            ModuleConfiguration\ConnectionType::MASTER,
+            $this->factoryStub,
+            $this->helpersManagerMock,
+            $this->repositoryMock
+        );
+
+        $this->expectException(StoreException::class);
+        $this->loggerMock->expects($this->once())->method('warning');
+
+        $store->persist($this->authenticationEvent);
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testGetActivityReturnsEmptyBagIfNoResults(): void
+    {
+        $this->repositoryMock->method('getActivity')->willReturn([]);
+
+        $store = new Store(
+            $this->moduleConfigurationStub,
+            $this->loggerMock,
+            null,
+            ModuleConfiguration\ConnectionType::MASTER,
+            $this->factoryStub,
+            $this->helpersManagerMock,
+            $this->repositoryMock
+        );
+
+        $activityBag = $store->getActivity('test', 10, 0);
+
+        $this->assertEmpty($activityBag->getAll());
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testCanGetActivityBag(): void
+    {
+        $this->repositoryMock->method('getActivity')
+            ->willReturn([RawRowResult::ACTIVITY]);
+
+        $store = new Store(
+            $this->moduleConfigurationStub,
+            $this->loggerMock,
+            null,
+            ModuleConfiguration\ConnectionType::MASTER,
+            $this->factoryStub,
+            $this->helpersManagerMock,
+            $this->repositoryMock
+        );
+
+        $activityBag = $store->getActivity('test', 10, 0);
+
+        $this->assertNotEmpty($activityBag->getAll());
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testGetActivityThrowsForInvalidResult(): void
+    {
+        $rawResult = RawRowResult::ACTIVITY;
+        unset($rawResult[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT]);
+
+        $this->repositoryMock->method('getActivity')
+            ->willReturn([$rawResult]);
+
+        $store = new Store(
+            $this->moduleConfigurationStub,
+            $this->loggerMock,
+            null,
+            ModuleConfiguration\ConnectionType::MASTER,
+            $this->factoryStub,
+            $this->helpersManagerMock,
+            $this->repositoryMock
+        );
+
+        $this->expectException(StoreException::class);
+        $store->getActivity('test', 10, 0);
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testCanDeleteDataOlderThan(): void
+    {
+        $dateTime = new DateTimeImmutable();
+
+        $this->repositoryMock->expects($this->once())
+            ->method('deleteAuthenticationEventsOlderThan')
+            ->with($dateTime);
+
+        $store = new Store(
+            $this->moduleConfigurationStub,
+            $this->loggerMock,
+            null,
+            ModuleConfiguration\ConnectionType::MASTER,
+            $this->factoryStub,
+            $this->helpersManagerMock,
+            $this->repositoryMock
+        );
+
+        $store->deleteDataOlderThan($dateTime);
+    }
+}
diff --git a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpSpUserVersionTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpSpUserVersionTableTest.php
new file mode 100644
index 0000000..cba763e
--- /dev/null
+++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpSpUserVersionTableTest.php
@@ -0,0 +1,103 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
+
+use Doctrine\DBAL\Exception;
+use Doctrine\DBAL\Schema\AbstractSchemaManager;
+use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
+use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
+
+/**
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpSpUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ */
+class CreateIdpSpUserVersionTableTest extends TestCase
+{
+    protected Connection $connection;
+    protected AbstractSchemaManager $schemaManager;
+    protected string $tableName;
+    protected Stub $connectionStub;
+    protected Stub $dbalStub;
+    protected Stub $schemaManagerStub;
+
+    /**
+     * @throws Exception
+     */
+    protected function setUp(): void
+    {
+        $this->connection = new Connection(ConnectionParameters::DBAL_SQLITE_MEMORY);
+        $this->schemaManager = $this->connection->dbal()->createSchemaManager();
+        $this->tableName = 'vds_idp_sp_user_version';
+
+        $this->connectionStub = $this->createStub(Connection::class);
+        $this->dbalStub = $this->createStub(\Doctrine\DBAL\Connection::class);
+        $this->schemaManagerStub = $this->createStub(AbstractSchemaManager::class);
+    }
+
+    /**
+     * @throws StoreException
+     * @throws MigrationException
+     * @throws Exception
+     */
+    public function testCanRunMigration(): void
+    {
+        $this->assertFalse($this->schemaManager->tablesExist($this->tableName));
+        $migration = new Migrations\CreateIdpSpUserVersionTable($this->connection);
+        $migration->run();
+        $this->assertTrue($this->schemaManager->tablesExist($this->tableName));
+        $migration->revert();
+        $this->assertFalse($this->schemaManager->tablesExist($this->tableName));
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testRunThrowsMigrationException(): void
+    {
+        $this->connectionStub->method('preparePrefixedTableName')->willReturn($this->tableName);
+        $this->schemaManagerStub->method('createTable')
+            ->willThrowException(new Exception('test'));
+        $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub);
+        $this->connectionStub->method('dbal')->willReturn($this->dbalStub);
+
+        $migration = new Migrations\CreateIdpSpUserVersionTable($this->connectionStub);
+        $this->expectException(MigrationException::class);
+        $migration->run();
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testRevertThrowsMigrationException(): void
+    {
+        $this->schemaManagerStub->method('dropTable')
+            ->willThrowException(new Exception('test'));
+        $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub);
+        $this->connectionStub->method('dbal')->willReturn($this->dbalStub);
+
+        $migration = new Migrations\CreateIdpSpUserVersionTable($this->connectionStub);
+        $this->expectException(MigrationException::class);
+        $migration->revert();
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testRunThrowsOnIvalidTableNameIdp(): void
+    {
+        $this->connectionStub->method('preparePrefixedTableName')
+            ->willReturnOnConsecutiveCalls(''); // Invalid (empty) name for table
+
+        $migration = new Migrations\CreateIdpSpUserVersionTable($this->connectionStub);
+        $this->expectException(MigrationException::class);
+        $migration->run();
+    }
+}
diff --git a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpTableTest.php
similarity index 74%
rename from tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTableTest.php
rename to tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpTableTest.php
index 4c93391..41792f8 100644
--- a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000000CreateIdpTableTest.php
+++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpTableTest.php
@@ -2,24 +2,24 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
 
 use Doctrine\DBAL\Exception;
 use Doctrine\DBAL\Schema\AbstractSchemaManager;
 use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpTable;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use PHPUnit\Framework\TestCase;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000000CreateIdpTable
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
  */
-class Version20220801000000CreateIdpTableTest extends TestCase
+class CreateIdpTableTest extends TestCase
 {
     protected Connection $connection;
     protected AbstractSchemaManager $schemaManager;
@@ -50,7 +50,7 @@ class Version20220801000000CreateIdpTableTest extends TestCase
     public function testCanRunMigration(): void
     {
         $this->assertFalse($this->schemaManager->tablesExist($this->tableName));
-        $migration = new Migrations\Version20220801000000CreateIdpTable($this->connection);
+        $migration = new CreateIdpTable($this->connection);
         $migration->run();
         $this->assertTrue($this->schemaManager->tablesExist($this->tableName));
         $migration->revert();
@@ -68,7 +68,7 @@ class Version20220801000000CreateIdpTableTest extends TestCase
         $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub);
         $this->connectionStub->method('dbal')->willReturn($this->dbalStub);
 
-        $migration = new Migrations\Version20220801000000CreateIdpTable($this->connectionStub);
+        $migration = new CreateIdpTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->run();
     }
@@ -83,7 +83,7 @@ class Version20220801000000CreateIdpTableTest extends TestCase
         $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub);
         $this->connectionStub->method('dbal')->willReturn($this->dbalStub);
 
-        $migration = new Migrations\Version20220801000000CreateIdpTable($this->connectionStub);
+        $migration = new CreateIdpTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->revert();
     }
@@ -96,7 +96,7 @@ class Version20220801000000CreateIdpTableTest extends TestCase
         $this->connectionStub->method('preparePrefixedTableName')
             ->willReturnOnConsecutiveCalls(''); // Invalid (empty) name for table
 
-        $migration = new Migrations\Version20220801000000CreateIdpTable($this->connectionStub);
+        $migration = new CreateIdpTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->run();
     }
diff --git a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpVersionTableTest.php
similarity index 73%
rename from tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTableTest.php
rename to tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpVersionTableTest.php
index 60e725d..cc8c079 100644
--- a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000100CreateIdpVersionTableTest.php
+++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateIdpVersionTableTest.php
@@ -2,24 +2,24 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
 
 use Doctrine\DBAL\Exception;
 use Doctrine\DBAL\Schema\AbstractSchemaManager;
 use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use PHPUnit\Framework\TestCase;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000100CreateIdpVersionTable
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection *
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection *
  */
-class Version20220801000100CreateIdpVersionTableTest extends TestCase
+class CreateIdpVersionTableTest extends TestCase
 {
     protected Connection $connection;
     protected AbstractSchemaManager $schemaManager;
@@ -50,7 +50,7 @@ class Version20220801000100CreateIdpVersionTableTest extends TestCase
     public function testCanRunMigration(): void
     {
         $this->assertFalse($this->schemaManager->tablesExist($this->tableName));
-        $migration = new Migrations\Version20220801000100CreateIdpVersionTable($this->connection);
+        $migration = new Migrations\CreateIdpVersionTable($this->connection);
         $migration->run();
         $this->assertTrue($this->schemaManager->tablesExist($this->tableName));
         $migration->revert();
@@ -68,7 +68,7 @@ class Version20220801000100CreateIdpVersionTableTest extends TestCase
         $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub);
         $this->connectionStub->method('dbal')->willReturn($this->dbalStub);
 
-        $migration = new Migrations\Version20220801000100CreateIdpVersionTable($this->connectionStub);
+        $migration = new Migrations\CreateIdpVersionTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->run();
     }
@@ -83,7 +83,7 @@ class Version20220801000100CreateIdpVersionTableTest extends TestCase
         $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub);
         $this->connectionStub->method('dbal')->willReturn($this->dbalStub);
 
-        $migration = new Migrations\Version20220801000100CreateIdpVersionTable($this->connectionStub);
+        $migration = new Migrations\CreateIdpVersionTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->revert();
     }
@@ -96,7 +96,7 @@ class Version20220801000100CreateIdpVersionTableTest extends TestCase
         $this->connectionStub->method('preparePrefixedTableName')
             ->willReturnOnConsecutiveCalls(''); // Invalid (empty) name for table
 
-        $migration = new Migrations\Version20220801000100CreateIdpVersionTable($this->connectionStub);
+        $migration = new Migrations\CreateIdpVersionTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->run();
     }
diff --git a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpTableTest.php
similarity index 74%
rename from tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTableTest.php
rename to tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpTableTest.php
index 66eb8ec..a388427 100644
--- a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000200CreateSpTableTest.php
+++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpTableTest.php
@@ -2,24 +2,24 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
 
 use Doctrine\DBAL\Exception;
 use Doctrine\DBAL\Schema\AbstractSchemaManager;
 use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateSpTable;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
-use PHPUnit\Framework\TestCase;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000200CreateSpTable
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection *
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateSpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
  */
-class Version20220801000200CreateSpTableTest extends TestCase
+class CreateSpTableTest extends TestCase
 {
     protected Connection $connection;
     protected AbstractSchemaManager $schemaManager;
@@ -50,7 +50,7 @@ class Version20220801000200CreateSpTableTest extends TestCase
     public function testCanRunMigration(): void
     {
         $this->assertFalse($this->schemaManager->tablesExist($this->tableName));
-        $migration = new Migrations\Version20220801000200CreateSpTable($this->connection);
+        $migration = new CreateSpTable($this->connection);
         $migration->run();
         $this->assertTrue($this->schemaManager->tablesExist($this->tableName));
         $migration->revert();
@@ -68,7 +68,7 @@ class Version20220801000200CreateSpTableTest extends TestCase
         $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub);
         $this->connectionStub->method('dbal')->willReturn($this->dbalStub);
 
-        $migration = new Migrations\Version20220801000200CreateSpTable($this->connectionStub);
+        $migration = new CreateSpTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->run();
     }
@@ -83,7 +83,7 @@ class Version20220801000200CreateSpTableTest extends TestCase
         $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub);
         $this->connectionStub->method('dbal')->willReturn($this->dbalStub);
 
-        $migration = new Migrations\Version20220801000200CreateSpTable($this->connectionStub);
+        $migration = new CreateSpTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->revert();
     }
@@ -96,7 +96,7 @@ class Version20220801000200CreateSpTableTest extends TestCase
         $this->connectionStub->method('preparePrefixedTableName')
             ->willReturnOnConsecutiveCalls(''); // Invalid (empty) name for table
 
-        $migration = new Migrations\Version20220801000200CreateSpTable($this->connectionStub);
+        $migration = new CreateSpTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->run();
     }
diff --git a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpVersionTableTest.php
similarity index 73%
rename from tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTableTest.php
rename to tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpVersionTableTest.php
index ebbf6d2..44e4151 100644
--- a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000300CreateSpVersionTableTest.php
+++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateSpVersionTableTest.php
@@ -2,24 +2,24 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
 
 use Doctrine\DBAL\Exception;
 use Doctrine\DBAL\Schema\AbstractSchemaManager;
 use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use PHPUnit\Framework\TestCase;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000300CreateSpVersionTable
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection *
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateSpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
  */
-class Version20220801000300CreateSpVersionTableTest extends TestCase
+class CreateSpVersionTableTest extends TestCase
 {
     protected Connection $connection;
     protected AbstractSchemaManager $schemaManager;
@@ -50,7 +50,7 @@ class Version20220801000300CreateSpVersionTableTest extends TestCase
     public function testCanRunMigration(): void
     {
         $this->assertFalse($this->schemaManager->tablesExist($this->tableName));
-        $migration = new Migrations\Version20220801000300CreateSpVersionTable($this->connection);
+        $migration = new Migrations\CreateSpVersionTable($this->connection);
         $migration->run();
         $this->assertTrue($this->schemaManager->tablesExist($this->tableName));
         $migration->revert();
@@ -68,7 +68,7 @@ class Version20220801000300CreateSpVersionTableTest extends TestCase
         $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub);
         $this->connectionStub->method('dbal')->willReturn($this->dbalStub);
 
-        $migration = new Migrations\Version20220801000300CreateSpVersionTable($this->connectionStub);
+        $migration = new Migrations\CreateSpVersionTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->run();
     }
@@ -83,7 +83,7 @@ class Version20220801000300CreateSpVersionTableTest extends TestCase
         $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub);
         $this->connectionStub->method('dbal')->willReturn($this->dbalStub);
 
-        $migration = new Migrations\Version20220801000300CreateSpVersionTable($this->connectionStub);
+        $migration = new Migrations\CreateSpVersionTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->revert();
     }
@@ -96,7 +96,7 @@ class Version20220801000300CreateSpVersionTableTest extends TestCase
         $this->connectionStub->method('preparePrefixedTableName')
             ->willReturnOnConsecutiveCalls(''); // Invalid (empty) name for table
 
-        $migration = new Migrations\Version20220801000300CreateSpVersionTable($this->connectionStub);
+        $migration = new Migrations\CreateSpVersionTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->run();
     }
diff --git a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserTableTest.php
similarity index 73%
rename from tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTableTest.php
rename to tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserTableTest.php
index f20463f..786ff8b 100644
--- a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000400CreateUserTableTest.php
+++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserTableTest.php
@@ -2,24 +2,25 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
 
 use Doctrine\DBAL\Exception;
 use Doctrine\DBAL\Schema\AbstractSchemaManager;
 use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use PHPUnit\Framework\TestCase;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000400CreateUserTable
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection *
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000400CreateUserTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
  */
-class Version20220801000400CreateUserTableTest extends TestCase
+class CreateUserTableTest extends TestCase
 {
     protected Connection $connection;
     protected AbstractSchemaManager $schemaManager;
@@ -50,7 +51,7 @@ class Version20220801000400CreateUserTableTest extends TestCase
     public function testCanRunMigration(): void
     {
         $this->assertFalse($this->schemaManager->tablesExist($this->tableName));
-        $migration = new Migrations\Version20220801000400CreateUserTable($this->connection);
+        $migration = new CreateUserTable($this->connection);
         $migration->run();
         $this->assertTrue($this->schemaManager->tablesExist($this->tableName));
         $migration->revert();
@@ -68,7 +69,7 @@ class Version20220801000400CreateUserTableTest extends TestCase
         $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub);
         $this->connectionStub->method('dbal')->willReturn($this->dbalStub);
 
-        $migration = new Migrations\Version20220801000400CreateUserTable($this->connectionStub);
+        $migration = new CreateUserTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->run();
     }
@@ -83,7 +84,7 @@ class Version20220801000400CreateUserTableTest extends TestCase
         $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub);
         $this->connectionStub->method('dbal')->willReturn($this->dbalStub);
 
-        $migration = new Migrations\Version20220801000400CreateUserTable($this->connectionStub);
+        $migration = new CreateUserTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->revert();
     }
@@ -96,7 +97,7 @@ class Version20220801000400CreateUserTableTest extends TestCase
         $this->connectionStub->method('preparePrefixedTableName')
             ->willReturnOnConsecutiveCalls(''); // Invalid (empty) name for table
 
-        $migration = new Migrations\Version20220801000400CreateUserTable($this->connectionStub);
+        $migration = new CreateUserTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->run();
     }
diff --git a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTableTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserVersionTableTest.php
similarity index 73%
rename from tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTableTest.php
rename to tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserVersionTableTest.php
index 8a6cf64..a151504 100644
--- a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000500CreateUserVersionTableTest.php
+++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/Migrations/CreateUserVersionTableTest.php
@@ -2,24 +2,24 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
 
 use Doctrine\DBAL\Exception;
 use Doctrine\DBAL\Schema\AbstractSchemaManager;
 use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use PHPUnit\Framework\TestCase;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000500CreateUserVersionTable
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection *
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
  */
-class Version20220801000500CreateUserVersionTableTest extends TestCase
+class CreateUserVersionTableTest extends TestCase
 {
     protected Connection $connection;
     protected AbstractSchemaManager $schemaManager;
@@ -50,7 +50,7 @@ class Version20220801000500CreateUserVersionTableTest extends TestCase
     public function testCanRunMigration(): void
     {
         $this->assertFalse($this->schemaManager->tablesExist($this->tableName));
-        $migration = new Migrations\Version20220801000500CreateUserVersionTable($this->connection);
+        $migration = new Migrations\CreateUserVersionTable($this->connection);
         $migration->run();
         $this->assertTrue($this->schemaManager->tablesExist($this->tableName));
         $migration->revert();
@@ -68,7 +68,7 @@ class Version20220801000500CreateUserVersionTableTest extends TestCase
         $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub);
         $this->connectionStub->method('dbal')->willReturn($this->dbalStub);
 
-        $migration = new Migrations\Version20220801000500CreateUserVersionTable($this->connectionStub);
+        $migration = new Migrations\CreateUserVersionTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->run();
     }
@@ -83,7 +83,7 @@ class Version20220801000500CreateUserVersionTableTest extends TestCase
         $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub);
         $this->connectionStub->method('dbal')->willReturn($this->dbalStub);
 
-        $migration = new Migrations\Version20220801000500CreateUserVersionTable($this->connectionStub);
+        $migration = new Migrations\CreateUserVersionTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->revert();
     }
@@ -96,7 +96,7 @@ class Version20220801000500CreateUserVersionTableTest extends TestCase
         $this->connectionStub->method('preparePrefixedTableName')
             ->willReturnOnConsecutiveCalls(''); // Invalid (empty) name for table
 
-        $migration = new Migrations\Version20220801000500CreateUserVersionTable($this->connectionStub);
+        $migration = new Migrations\CreateUserVersionTable($this->connectionStub);
         $this->expectException(MigrationException::class);
         $migration->run();
     }
diff --git a/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/RepositoryTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/RepositoryTest.php
new file mode 100644
index 0000000..2007a26
--- /dev/null
+++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/Store/RepositoryTest.php
@@ -0,0 +1,394 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store;
+
+use DateTimeImmutable;
+use Exception;
+use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants
+    as BaseTableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator;
+use SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
+use SimpleSAML\Test\Module\accounting\Constants\DateTime;
+
+/**
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Helpers\Filesystem
+ * @uses \SimpleSAML\Module\accounting\ModuleConfiguration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateSpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateSpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpSpUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
+ */
+class RepositoryTest extends TestCase
+{
+    protected Connection $connection;
+    protected \Doctrine\DBAL\Connection $dbal;
+    /**
+     * @var Stub
+     */
+    protected $loggerStub;
+    protected Migrator $migrator;
+    protected string $dateTimeFormat;
+    protected string $idpEntityId;
+    protected string $idpEntityIdHash;
+    protected string $idpMetadata;
+    protected string $idpMetadataHash;
+    protected string $spEntityId;
+    protected string $spMetadataHash;
+    protected string $userIdentifier;
+    protected string $userIdentifierHash;
+    protected string $userAttributes;
+    protected string $userAttributesHash;
+    protected Repository $repository;
+    protected DateTimeImmutable $createdAt;
+    /**
+     * @var Stub
+     */
+    protected $connectionStub;
+    protected string $spEntityIdHash;
+    protected string $spMetadata;
+    protected string $clientIpAddress;
+    protected string $authenticationProtocolDesignation;
+
+    /**
+     * @throws StoreException
+     * @throws MigrationException
+     */
+    protected function setUp(): void
+    {
+        // For stubbing.
+        $this->connectionStub = $this->createStub(Connection::class);
+        $this->loggerStub = $this->createStub(LoggerInterface::class);
+
+        // For real DB testing.
+        $connectionParameters = ConnectionParameters::DBAL_SQLITE_MEMORY;
+        $this->connection = new Connection($connectionParameters);
+        $this->migrator = new Migrator($this->connection, $this->loggerStub);
+        $moduleConfiguration = new ModuleConfiguration();
+        $migrationsDirectory = $moduleConfiguration->getModuleSourceDirectory() . DIRECTORY_SEPARATOR .
+            'Data' . DIRECTORY_SEPARATOR .
+            'Stores' . DIRECTORY_SEPARATOR .
+            'Accounting' . DIRECTORY_SEPARATOR .
+            'Bases' . DIRECTORY_SEPARATOR .
+            'DoctrineDbal' . DIRECTORY_SEPARATOR .
+            'Versioned' . DIRECTORY_SEPARATOR .
+            'Store' . DIRECTORY_SEPARATOR .
+            AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME;
+        $namespace = Store::class . '\\' . AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME;
+
+        $this->migrator->runSetup();
+        $this->migrator->runNonImplementedMigrationClasses($migrationsDirectory, $namespace);
+
+        $this->repository = new Repository($this->connection, $this->loggerStub);
+
+        $this->dateTimeFormat = DateTime::DEFAULT_FORMAT;
+
+        $this->idpEntityId = 'idp-entity-id';
+        $this->idpEntityIdHash = 'idp-entity-id-hash';
+
+        $this->idpMetadata = 'idp-metadata';
+        $this->idpMetadataHash = 'idp-metadata-hash';
+
+        $this->spEntityId = 'sp-entity-id';
+        $this->spEntityIdHash = 'sp-entity-id-hash';
+
+        $this->spMetadata = 'sp-metadata';
+        $this->spMetadataHash = 'sp-metadata-hash';
+
+        $this->userIdentifier = 'user-identifier';
+        $this->userIdentifierHash = 'user-identifier-hash';
+
+        $this->userAttributes = 'user-attributes';
+        $this->userAttributesHash = 'user-attributes-hash';
+
+        $this->createdAt = new DateTimeImmutable();
+        $this->clientIpAddress = '123.123.123.123';
+        $this->authenticationProtocolDesignation = Saml2::DESIGNATION;
+    }
+
+    public function testCanCreateInstance(): void
+    {
+        $this->assertInstanceOf(
+            Repository::class,
+            new Repository($this->connection, $this->loggerStub)
+        );
+    }
+
+    /**
+     * @throws StoreException
+     * @throws \Doctrine\DBAL\Exception
+     */
+    public function testCanInsertAndGetIdp(): array
+    {
+        $this->repository->insertIdp($this->idpEntityId, $this->idpEntityIdHash, $this->createdAt);
+
+        $result = $this->repository->getIdp($this->idpEntityIdHash)->fetchAssociative();
+
+        $this->assertSame($this->idpEntityId, $result[BaseTableConstants::TABLE_IDP_COLUMN_NAME_ENTITY_ID]);
+        $this->assertSame(
+            $this->idpEntityIdHash,
+            $result[BaseTableConstants::TABLE_IDP_COLUMN_NAME_ENTITY_ID_HASH_SHA256]
+        );
+        $this->assertSame(
+            $this->createdAt->format($this->dateTimeFormat),
+            $result[BaseTableConstants::TABLE_IDP_COLUMN_NAME_CREATED_AT]
+        );
+
+        return $result;
+    }
+
+    public function testInsertIdpThrowsOnNonUniqueIdpEntityIdHash(): void
+    {
+        $this->expectException(StoreException::class);
+
+        // Can't insert duplicate idp entity ID hash.
+        $this->repository->insertIdp($this->idpEntityId, $this->idpEntityIdHash, $this->createdAt);
+        $this->repository->insertIdp($this->idpEntityId, $this->idpEntityIdHash, $this->createdAt);
+    }
+
+    public function testGetIdpThrowsOnInvalidDbal(): void
+    {
+        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
+        $repository = new Repository($this->connectionStub, $this->loggerStub);
+        $this->expectException(StoreException::class);
+
+        $repository->getIdp($this->idpEntityIdHash);
+    }
+
+    /**
+     * @depends testCanInsertAndGetIdp
+     * @throws StoreException
+     * @throws StoreException
+     * @throws \Doctrine\DBAL\Exception
+     */
+    public function testCanInsertAndGetIdpVersion(array $idpResult): array
+    {
+        $idpId = (int)$idpResult[BaseTableConstants::TABLE_IDP_COLUMN_NAME_ID];
+
+        $this->repository->insertIdpVersion($idpId, $this->idpMetadata, $this->idpMetadataHash, $this->createdAt);
+
+        $result = $this->repository->getIdpVersion($idpId, $this->idpMetadataHash)->fetchAssociative();
+
+        $this->assertSame($this->idpMetadata, $result[BaseTableConstants::TABLE_IDP_VERSION_COLUMN_NAME_METADATA]);
+        $this->assertSame(
+            $this->idpMetadataHash,
+            $result[BaseTableConstants::TABLE_IDP_VERSION_COLUMN_NAME_METADATA_HASH_SHA256]
+        );
+        $this->assertSame(
+            $this->createdAt->format($this->dateTimeFormat),
+            $result[BaseTableConstants::TABLE_IDP_VERSION_COLUMN_NAME_CREATED_AT]
+        );
+
+        return $result;
+    }
+
+    public function testInsertIdpVersionThrowsOnNonUniqueIdpMetadataHash(): void
+    {
+        $this->expectException(StoreException::class);
+        // IdP Metadata Hash must be unique.
+        $this->repository->insertIdpVersion(1, $this->idpMetadata, $this->idpMetadataHash, $this->createdAt);
+        $this->repository->insertIdpVersion(1, $this->idpMetadata, $this->idpMetadataHash, $this->createdAt);
+    }
+
+    public function testGetIdpVersionThrowsOnInvalidDbal(): void
+    {
+        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
+        $repository = new Repository($this->connectionStub, $this->loggerStub);
+        $this->expectException(StoreException::class);
+
+        $repository->getIdpVersion(1, $this->idpMetadataHash);
+    }
+
+    /**
+     * @throws StoreException
+     * @throws \Doctrine\DBAL\Exception
+     */
+    public function testCanInsertAndGetSp(): array
+    {
+        $this->repository->insertSp($this->spEntityId, $this->spEntityIdHash, $this->createdAt);
+
+        $result = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative();
+
+        $this->assertSame($this->spEntityId, $result[BaseTableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID]);
+        $this->assertSame(
+            $this->spEntityIdHash,
+            $result[BaseTableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID_HASH_SHA256]
+        );
+        $this->assertSame(
+            $this->createdAt->format($this->dateTimeFormat),
+            $result[BaseTableConstants::TABLE_SP_COLUMN_NAME_CREATED_AT]
+        );
+
+        return $result;
+    }
+
+    public function testInsertSpThrowsOnNonUniqueSpEntityIdHash(): void
+    {
+        $this->expectException(StoreException::class);
+        // SP Entity ID Hash must be unique.
+        $this->repository->insertSp($this->spEntityId, $this->spEntityIdHash, $this->createdAt);
+        $this->repository->insertSp($this->spEntityId, $this->spEntityIdHash, $this->createdAt);
+    }
+
+    public function testGetSpThrowsOnInvalidDbal(): void
+    {
+        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
+        $repository = new Repository($this->connectionStub, $this->loggerStub);
+        $this->expectException(StoreException::class);
+
+        $repository->getSp($this->spEntityIdHash);
+    }
+
+    /**
+     * @depends testCanInsertAndGetSp
+     * @throws StoreException
+     * @throws StoreException
+     * @throws \Doctrine\DBAL\Exception
+     */
+    public function testCanInsertAndGetSpVersion(array $spResult): array
+    {
+        $spId = (int)$spResult[BaseTableConstants::TABLE_SP_COLUMN_NAME_ID];
+
+        $this->repository->insertSpVersion($spId, $this->spMetadata, $this->spMetadataHash, $this->createdAt);
+
+        $result = $this->repository->getSpVersion($spId, $this->spMetadataHash)->fetchAssociative();
+
+        $this->assertSame($this->spMetadata, $result[BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_METADATA]);
+        $this->assertSame(
+            $this->spMetadataHash,
+            $result[BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_METADATA_HASH_SHA256]
+        );
+        $this->assertSame(
+            $this->createdAt->format($this->dateTimeFormat),
+            $result[BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_CREATED_AT]
+        );
+
+        return $result;
+    }
+
+    public function testInsertSpVersionThrowsOnNonUniqueMetadataHash(): void
+    {
+        $this->expectException(StoreException::class);
+        // SP metadata hash must be unique.
+        $this->repository->insertSpVersion(1, $this->spMetadata, $this->spMetadataHash, $this->createdAt);
+        $this->repository->insertSpVersion(1, $this->spMetadata, $this->spMetadataHash, $this->createdAt);
+    }
+
+    public function testGetSpVersionThrowsOnInvalidDbal(): void
+    {
+        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
+        $repository = new Repository($this->connectionStub, $this->loggerStub);
+        $this->expectException(StoreException::class);
+
+        $repository->getSpVersion(1, $this->spMetadataHash);
+    }
+
+    /**
+     * @throws StoreException
+     * @throws \Doctrine\DBAL\Exception
+     */
+    public function testCanInsertAndGetUser(): array
+    {
+        $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt);
+
+        $result = $this->repository->getUser($this->userIdentifierHash)->fetchAssociative();
+
+        $this->assertSame($this->userIdentifier, $result[BaseTableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER]);
+        $this->assertSame(
+            $this->userIdentifierHash,
+            $result[BaseTableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256]
+        );
+        $this->assertSame(
+            $this->createdAt->format($this->dateTimeFormat),
+            $result[BaseTableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT]
+        );
+
+        return $result;
+    }
+
+    public function testInsertUserThrowsOnNonUniqueIdentifierHash(): void
+    {
+        $this->expectException(StoreException::class);
+        $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt);
+        $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt);
+    }
+
+    public function testGetUserThrowsOnInvalidDbal(): void
+    {
+        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
+        $repository = new Repository($this->connectionStub, $this->loggerStub);
+        $this->expectException(StoreException::class);
+
+        $repository->getUser($this->userIdentifierHash);
+    }
+
+    /**
+     * @depends testCanInsertAndGetUser
+     * @throws StoreException
+     * @throws StoreException
+     * @throws \Doctrine\DBAL\Exception
+     */
+    public function testCanInsertAndGetUserVersion(array $userResult): array
+    {
+        $userId = (int)$userResult[BaseTableConstants::TABLE_USER_COLUMN_NAME_ID];
+
+        $this->repository
+            ->insertUserVersion($userId, $this->userAttributes, $this->userAttributesHash, $this->createdAt);
+
+        $result = $this->repository->getUserVersion($userId, $this->userAttributesHash)->fetchAssociative();
+
+        $this->assertSame(
+            $this->userAttributes,
+            $result[BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES]
+        );
+        $this->assertSame(
+            $this->userAttributesHash,
+            $result[BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256]
+        );
+        $this->assertSame(
+            $this->createdAt->format($this->dateTimeFormat),
+            $result[BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT]
+        );
+
+        return $result;
+    }
+
+    public function testInsertUserVersionThrowsOnNonUniqueAttributesHash(): void
+    {
+        $this->expectException(StoreException::class);
+        $this->repository
+            ->insertUserVersion(1, $this->userAttributes, $this->userAttributesHash, $this->createdAt);
+        $this->repository
+            ->insertUserVersion(1, $this->userAttributes, $this->userAttributesHash, $this->createdAt);
+    }
+
+    public function testGetUserVersionThrowsOnInvalidDbal(): void
+    {
+        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
+        $repository = new Repository($this->connectionStub, $this->loggerStub);
+        $this->expectException(StoreException::class);
+
+        $repository->getUserVersion(1, $this->userIdentifierHash);
+    }
+}
diff --git a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/StoreTest.php b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/StoreTest.php
similarity index 56%
rename from tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/StoreTest.php
rename to tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/StoreTest.php
index df3cdf4..10c9068 100644
--- a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/StoreTest.php
+++ b/tests/src/Data/Stores/Accounting/Bases/DoctrineDbal/Versioned/StoreTest.php
@@ -2,66 +2,61 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned;
 
-use DateTimeImmutable;
 use Doctrine\DBAL\Result;
 use Exception;
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event\State;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
 use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
-use PHPUnit\Framework\TestCase;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\TableConstants;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
-use SimpleSAML\Test\Module\accounting\Constants\RawRowResult;
 use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractStore
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Repository
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event
  * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractState
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event\State\Saml2
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000000CreateIdpTable
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000100CreateIdpVersionTable
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000200CreateSpTable
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000300CreateSpVersionTable
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000400CreateUserTable
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000500CreateUserVersionTable
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000600CreateIdpSpUserVersionTable
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000700CreateAuthenticationEventTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateSpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateSpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpSpUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore
  * @uses \SimpleSAML\Module\accounting\Helpers\Filesystem
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\HashDecoratedState
  * @uses \SimpleSAML\Module\accounting\Helpers\Hash
  * @uses \SimpleSAML\Module\accounting\Helpers\Arr
  * @uses \SimpleSAML\Module\accounting\Helpers\Network
- * @uses \SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider\Bag
  * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractProvider
- * @uses \SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider
  * @uses \SimpleSAML\Module\accounting\Entities\User
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractRawEntity
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\RawConnectedServiceProvider
- * @uses \SimpleSAML\Module\accounting\Entities\Activity\Bag
- * @uses \SimpleSAML\Module\accounting\Entities\Activity
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\RawActivity
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity
  * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore
  * @uses \SimpleSAML\Module\accounting\Entities\Providers\Service\Saml2
  * @uses \SimpleSAML\Module\accounting\Helpers\ProviderResolver
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2
@@ -74,7 +69,7 @@ class StoreTest extends TestCase
     protected Connection $connection;
     protected State\Saml2 $state;
     protected Event $authenticationEvent;
-    protected Store\HashDecoratedState $hashDecoratedState;
+    protected HashDecoratedState $hashDecoratedState;
     /**
      * @var MockObject
      */
@@ -87,6 +82,10 @@ class StoreTest extends TestCase
      * @var MockObject
      */
     protected $loggerMock;
+    /**
+     * @var MockObject
+     */
+    protected $helpersManagerMock;
 
     /**
      * @throws StoreException
@@ -113,10 +112,13 @@ class StoreTest extends TestCase
         $this->state = new State\Saml2(StateArrays::SAML2_FULL);
         $this->authenticationEvent = new Event($this->state);
 
-        $this->hashDecoratedState = new Store\HashDecoratedState($this->state);
-        $this->repositoryMock = $this->createMock(Store\Repository::class);
+        $this->hashDecoratedState = new HashDecoratedState($this->state);
+        $this->repositoryMock = $this->createMock(
+            \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository::class
+        );
 
         $this->resultStub = $this->createStub(Result::class);
+        $this->helpersManagerMock = $this->createMock(HelpersManager::class);
     }
 
     /**
@@ -152,7 +154,7 @@ class StoreTest extends TestCase
      * @throws \Doctrine\DBAL\Exception
      * @throws MigrationException
      */
-    public function testCanPersistAuthenticationEvent(): void
+    public function testCanPersistVersionedData(): void
     {
         $store = new Store(
             $this->moduleConfigurationStub,
@@ -169,59 +171,43 @@ class StoreTest extends TestCase
         $spVersionCountQueryBuilder = $this->connection->dbal()->createQueryBuilder();
         $userCountQueryBuilder = $this->connection->dbal()->createQueryBuilder();
         $userVersionCountQueryBuilder = $this->connection->dbal()->createQueryBuilder();
-        $idpSpUserVersionCountQueryBuilder = $this->connection->dbal()->createQueryBuilder();
-        $authenticationEventCountQueryBuilder = $this->connection->dbal()->createQueryBuilder();
 
         $idpCountQueryBuilder->select('COUNT(id) as idpCount')->from(
             //'vds_idp'
             $this->connection->preparePrefixedTableName(
-                Store\TableConstants::TABLE_PREFIX . Store\TableConstants::TABLE_NAME_IDP
+                TableConstants::TABLE_PREFIX . TableConstants::TABLE_NAME_IDP
             )
         );
         $idpVersionCountQueryBuilder->select('COUNT(id) as idpVersionCount')->from(
             //'vds_idp_version'
             $this->connection->preparePrefixedTableName(
-                Store\TableConstants::TABLE_PREFIX . Store\TableConstants::TABLE_NAME_IDP_VERSION
+                TableConstants::TABLE_PREFIX . TableConstants::TABLE_NAME_IDP_VERSION
             )
         );
         $spCountQueryBuilder->select('COUNT(id) as spCount')->from(
             //'vds_sp'
             $this->connection->preparePrefixedTableName(
-                Store\TableConstants::TABLE_PREFIX . Store\TableConstants::TABLE_NAME_SP
+                TableConstants::TABLE_PREFIX . TableConstants::TABLE_NAME_SP
             )
         );
         $spVersionCountQueryBuilder->select('COUNT(id) as spVersionCount')->from(
             //'vds_sp_version'
             $this->connection->preparePrefixedTableName(
-                Store\TableConstants::TABLE_PREFIX . Store\TableConstants::TABLE_NAME_SP_VERSION
+                TableConstants::TABLE_PREFIX . TableConstants::TABLE_NAME_SP_VERSION
             )
         );
         $userCountQueryBuilder->select('COUNT(id) as userCount')->from(
             //'vds_user'
             $this->connection->preparePrefixedTableName(
-                Store\TableConstants::TABLE_PREFIX . Store\TableConstants::TABLE_NAME_USER
+                TableConstants::TABLE_PREFIX . TableConstants::TABLE_NAME_USER
             )
         );
         $userVersionCountQueryBuilder->select('COUNT(id) as userVersionCount')->from(
             //'vds_user_version'
             $this->connection->preparePrefixedTableName(
-                Store\TableConstants::TABLE_PREFIX . Store\TableConstants::TABLE_NAME_USER_VERSION
+                TableConstants::TABLE_PREFIX . TableConstants::TABLE_NAME_USER_VERSION
             )
         );
-        $idpSpUserVersionCountQueryBuilder->select('COUNT(id) as idpSpUserVersionCount')
-            ->from(
-                //'vds_idp_sp_user_version'
-                $this->connection->preparePrefixedTableName(
-                    Store\TableConstants::TABLE_PREFIX . Store\TableConstants::TABLE_NAME_IDP_SP_USER_VERSION
-                )
-            );
-        $authenticationEventCountQueryBuilder->select('COUNT(id) as authenticationEventCount')
-            ->from(
-                //'vds_authentication_event'
-                $this->connection->preparePrefixedTableName(
-                    Store\TableConstants::TABLE_PREFIX . Store\TableConstants::TABLE_NAME_AUTHENTICATION_EVENT
-                )
-            );
 
         $this->assertSame(0, (int)$idpCountQueryBuilder->executeQuery()->fetchOne());
         $this->assertSame(0, (int)$idpVersionCountQueryBuilder->executeQuery()->fetchOne());
@@ -229,10 +215,13 @@ class StoreTest extends TestCase
         $this->assertSame(0, (int)$spVersionCountQueryBuilder->executeQuery()->fetchOne());
         $this->assertSame(0, (int)$userCountQueryBuilder->executeQuery()->fetchOne());
         $this->assertSame(0, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne());
-        $this->assertSame(0, (int)$idpSpUserVersionCountQueryBuilder->executeQuery()->fetchOne());
-        $this->assertSame(0, (int)$authenticationEventCountQueryBuilder->executeQuery()->fetchOne());
 
-        $store->persist($this->authenticationEvent);
+        $idpId = $store->resolveIdpId($this->hashDecoratedState);
+        $store->resolveIdpVersionId($idpId, $this->hashDecoratedState);
+        $spId = $store->resolveSpId($this->hashDecoratedState);
+        $store->resolveSpVersionId($spId, $this->hashDecoratedState);
+        $userId = $store->resolveUserId($this->hashDecoratedState);
+        $store->resolveUserVersionId($userId, $this->hashDecoratedState);
 
         $this->assertSame(1, (int)$idpCountQueryBuilder->executeQuery()->fetchOne());
         $this->assertSame(1, (int)$idpVersionCountQueryBuilder->executeQuery()->fetchOne());
@@ -240,10 +229,13 @@ class StoreTest extends TestCase
         $this->assertSame(1, (int)$spVersionCountQueryBuilder->executeQuery()->fetchOne());
         $this->assertSame(1, (int)$userCountQueryBuilder->executeQuery()->fetchOne());
         $this->assertSame(1, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne());
-        $this->assertSame(1, (int)$idpSpUserVersionCountQueryBuilder->executeQuery()->fetchOne());
-        $this->assertSame(1, (int)$authenticationEventCountQueryBuilder->executeQuery()->fetchOne());
 
-        $store->persist($this->authenticationEvent);
+        $idpId = $store->resolveIdpId($this->hashDecoratedState);
+        $store->resolveIdpVersionId($idpId, $this->hashDecoratedState);
+        $spId = $store->resolveSpId($this->hashDecoratedState);
+        $store->resolveSpVersionId($spId, $this->hashDecoratedState);
+        $userId = $store->resolveUserId($this->hashDecoratedState);
+        $store->resolveUserVersionId($userId, $this->hashDecoratedState);
 
         $this->assertSame(1, (int)$idpCountQueryBuilder->executeQuery()->fetchOne());
         $this->assertSame(1, (int)$idpVersionCountQueryBuilder->executeQuery()->fetchOne());
@@ -251,8 +243,6 @@ class StoreTest extends TestCase
         $this->assertSame(1, (int)$spVersionCountQueryBuilder->executeQuery()->fetchOne());
         $this->assertSame(1, (int)$userCountQueryBuilder->executeQuery()->fetchOne());
         $this->assertSame(1, (int)$userVersionCountQueryBuilder->executeQuery()->fetchOne());
-        $this->assertSame(1, (int)$idpSpUserVersionCountQueryBuilder->executeQuery()->fetchOne());
-        $this->assertSame(2, (int)$authenticationEventCountQueryBuilder->executeQuery()->fetchOne());
     }
 
     /**
@@ -267,12 +257,13 @@ class StoreTest extends TestCase
             null,
             ModuleConfiguration\ConnectionType::MASTER,
             $this->factoryStub,
+            $this->helpersManagerMock,
             $this->repositoryMock
         );
 
         $this->expectException(StoreException::class);
 
-        $store->persist($this->authenticationEvent);
+        $store->resolveIdpId($this->hashDecoratedState);
     }
 
     /**
@@ -290,13 +281,14 @@ class StoreTest extends TestCase
             null,
             ModuleConfiguration\ConnectionType::MASTER,
             $this->factoryStub,
+            $this->helpersManagerMock,
             $this->repositoryMock
         );
 
         $this->expectException(StoreException::class);
         $this->loggerMock->expects($this->once())->method('warning');
 
-        $store->persist($this->authenticationEvent);
+        $store->resolveIdpId($this->hashDecoratedState);
     }
 
     /**
@@ -311,12 +303,14 @@ class StoreTest extends TestCase
             null,
             ModuleConfiguration\ConnectionType::MASTER,
             $this->factoryStub,
+            $this->helpersManagerMock,
             $this->repositoryMock
         );
 
         $this->expectException(StoreException::class);
 
-        $store->persist($this->authenticationEvent);
+        $idpId = $store->resolveIdpId($this->hashDecoratedState);
+        $store->resolveIdpVersionId($idpId, $this->hashDecoratedState);
     }
 
     /**
@@ -334,13 +328,15 @@ class StoreTest extends TestCase
             null,
             ModuleConfiguration\ConnectionType::MASTER,
             $this->factoryStub,
+            $this->helpersManagerMock,
             $this->repositoryMock
         );
 
         $this->expectException(StoreException::class);
         $this->loggerMock->expects($this->once())->method('warning');
 
-        $store->persist($this->authenticationEvent);
+        $idpId = $store->resolveIdpId($this->hashDecoratedState);
+        $store->resolveIdpVersionId($idpId, $this->hashDecoratedState);
     }
 
     /**
@@ -355,12 +351,13 @@ class StoreTest extends TestCase
             null,
             ModuleConfiguration\ConnectionType::MASTER,
             $this->factoryStub,
+            $this->helpersManagerMock,
             $this->repositoryMock
         );
 
         $this->expectException(StoreException::class);
 
-        $store->persist($this->authenticationEvent);
+        $store->resolveSpId($this->hashDecoratedState);
     }
 
     /**
@@ -378,13 +375,14 @@ class StoreTest extends TestCase
             null,
             ModuleConfiguration\ConnectionType::MASTER,
             $this->factoryStub,
+            $this->helpersManagerMock,
             $this->repositoryMock
         );
 
         $this->expectException(StoreException::class);
         $this->loggerMock->expects($this->once())->method('warning');
 
-        $store->persist($this->authenticationEvent);
+        $store->resolveSpId($this->hashDecoratedState);
     }
 
     /**
@@ -399,12 +397,14 @@ class StoreTest extends TestCase
             null,
             ModuleConfiguration\ConnectionType::MASTER,
             $this->factoryStub,
+            $this->helpersManagerMock,
             $this->repositoryMock
         );
 
         $this->expectException(StoreException::class);
 
-        $store->persist($this->authenticationEvent);
+        $spId = $store->resolveSpId($this->hashDecoratedState);
+        $store->resolveSpVersionId($spId, $this->hashDecoratedState);
     }
 
     /**
@@ -422,13 +422,15 @@ class StoreTest extends TestCase
             null,
             ModuleConfiguration\ConnectionType::MASTER,
             $this->factoryStub,
+            $this->helpersManagerMock,
             $this->repositoryMock
         );
 
         $this->expectException(StoreException::class);
         $this->loggerMock->expects($this->once())->method('warning');
 
-        $store->persist($this->authenticationEvent);
+        $spId = $store->resolveSpId($this->hashDecoratedState);
+        $store->resolveSpVersionId($spId, $this->hashDecoratedState);
     }
 
     /**
@@ -445,12 +447,13 @@ class StoreTest extends TestCase
             null,
             ModuleConfiguration\ConnectionType::MASTER,
             $this->factoryStub,
+            $this->helpersManagerMock,
             $this->repositoryMock
         );
 
         $this->expectException(UnexpectedValueException::class);
 
-        $store->persist($this->authenticationEvent);
+        $store->resolveUserId($this->hashDecoratedState);
     }
 
     /**
@@ -465,12 +468,13 @@ class StoreTest extends TestCase
             null,
             ModuleConfiguration\ConnectionType::MASTER,
             $this->factoryStub,
+            $this->helpersManagerMock,
             $this->repositoryMock
         );
 
         $this->expectException(StoreException::class);
 
-        $store->persist($this->authenticationEvent);
+        $store->resolveUserId($this->hashDecoratedState);
     }
 
     /**
@@ -488,13 +492,14 @@ class StoreTest extends TestCase
             null,
             ModuleConfiguration\ConnectionType::MASTER,
             $this->factoryStub,
+            $this->helpersManagerMock,
             $this->repositoryMock
         );
 
         $this->expectException(StoreException::class);
         $this->loggerMock->expects($this->once())->method('warning');
 
-        $store->persist($this->authenticationEvent);
+        $store->resolveUserId($this->hashDecoratedState);
     }
 
     /**
@@ -509,12 +514,14 @@ class StoreTest extends TestCase
             null,
             ModuleConfiguration\ConnectionType::MASTER,
             $this->factoryStub,
+            $this->helpersManagerMock,
             $this->repositoryMock
         );
 
         $this->expectException(StoreException::class);
 
-        $store->persist($this->authenticationEvent);
+        $userId = $store->resolveUserId($this->hashDecoratedState);
+        $store->resolveUserVersionId($userId, $this->hashDecoratedState);
     }
 
     /**
@@ -532,213 +539,14 @@ class StoreTest extends TestCase
             null,
             ModuleConfiguration\ConnectionType::MASTER,
             $this->factoryStub,
+            $this->helpersManagerMock,
             $this->repositoryMock
         );
 
         $this->expectException(StoreException::class);
         $this->loggerMock->expects($this->once())->method('warning');
 
-        $store->persist($this->authenticationEvent);
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public function testResolveIdpSpUserVersionIdThrowsOnFirstGetIdpSpUserVersionFailure(): void
-    {
-        $this->repositoryMock->method('getIdpSpUserVersion')->willThrowException(new Exception('test'));
-        $store = new Store(
-            $this->moduleConfigurationStub,
-            $this->loggerMock,
-            null,
-            ModuleConfiguration\ConnectionType::MASTER,
-            $this->factoryStub,
-            $this->repositoryMock
-        );
-
-        $this->expectException(StoreException::class);
-
-        $store->persist($this->authenticationEvent);
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public function testResolveIdpSpUserVersionIdThrowsOnInsertAndGetIdpSpUserVersionFailure(): void
-    {
-        $this->resultStub->method('fetchOne')->willReturn(false);
-        $this->repositoryMock->method('getIdpSpUserVersion')->willReturn($this->resultStub);
-        $this->repositoryMock->method('insertIdpSpUserVersion')->willThrowException(new Exception('test'));
-
-        $store = new Store(
-            $this->moduleConfigurationStub,
-            $this->loggerMock,
-            null,
-            ModuleConfiguration\ConnectionType::MASTER,
-            $this->factoryStub,
-            $this->repositoryMock
-        );
-
-        $this->expectException(StoreException::class);
-        $this->loggerMock->expects($this->once())->method('warning');
-
-        $store->persist($this->authenticationEvent);
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public function testGetConnectedOrganizationsReturnsEmptyBagIfNoResults(): void
-    {
-        $this->repositoryMock->method('getConnectedServiceProviders')->willReturn([]);
-
-        $store = new Store(
-            $this->moduleConfigurationStub,
-            $this->loggerMock,
-            null,
-            ModuleConfiguration\ConnectionType::MASTER,
-            $this->factoryStub,
-            $this->repositoryMock
-        );
-
-        $connectedServiceProviderBag = $store->getConnectedOrganizations('test');
-
-        $this->assertEmpty($connectedServiceProviderBag->getAll());
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public function testCanGetConnectedOrganizationsBag(): void
-    {
-        $this->repositoryMock->method('getConnectedServiceProviders')
-            ->willReturn([RawRowResult::CONNECTED_ORGANIZATION]);
-
-        $store = new Store(
-            $this->moduleConfigurationStub,
-            $this->loggerMock,
-            null,
-            ModuleConfiguration\ConnectionType::MASTER,
-            $this->factoryStub,
-            $this->repositoryMock
-        );
-
-        $connectedServiceProviderBag = $store->getConnectedOrganizations('test');
-
-        $this->assertNotEmpty($connectedServiceProviderBag->getAll());
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public function testGetConnectedOrganizationsThrowsForInvalidResult(): void
-    {
-        $rawResult = RawRowResult::CONNECTED_ORGANIZATION;
-        unset($rawResult[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS]);
-
-        $this->repositoryMock->method('getConnectedServiceProviders')
-            ->willReturn([$rawResult]);
-
-        $store = new Store(
-            $this->moduleConfigurationStub,
-            $this->loggerMock,
-            null,
-            ModuleConfiguration\ConnectionType::MASTER,
-            $this->factoryStub,
-            $this->repositoryMock
-        );
-
-        $this->expectException(StoreException::class);
-        $store->getConnectedOrganizations('test');
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public function testGetActivityReturnsEmptyBagIfNoResults(): void
-    {
-        $this->repositoryMock->method('getActivity')->willReturn([]);
-
-        $store = new Store(
-            $this->moduleConfigurationStub,
-            $this->loggerMock,
-            null,
-            ModuleConfiguration\ConnectionType::MASTER,
-            $this->factoryStub,
-            $this->repositoryMock
-        );
-
-        $activityBag = $store->getActivity('test', 10, 0);
-
-        $this->assertEmpty($activityBag->getAll());
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public function testCanGetActivityBag(): void
-    {
-        $this->repositoryMock->method('getActivity')
-            ->willReturn([RawRowResult::ACTIVITY]);
-
-        $store = new Store(
-            $this->moduleConfigurationStub,
-            $this->loggerMock,
-            null,
-            ModuleConfiguration\ConnectionType::MASTER,
-            $this->factoryStub,
-            $this->repositoryMock
-        );
-
-        $activityBag = $store->getActivity('test', 10, 0);
-
-        $this->assertNotEmpty($activityBag->getAll());
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public function testGetActivityThrowsForInvalidResult(): void
-    {
-        $rawResult = RawRowResult::ACTIVITY;
-        unset($rawResult[TableConstants::ENTITY_ACTIVITY_COLUMN_NAME_HAPPENED_AT]);
-
-        $this->repositoryMock->method('getActivity')
-            ->willReturn([$rawResult]);
-
-        $store = new Store(
-            $this->moduleConfigurationStub,
-            $this->loggerMock,
-            null,
-            ModuleConfiguration\ConnectionType::MASTER,
-            $this->factoryStub,
-            $this->repositoryMock
-        );
-
-        $this->expectException(StoreException::class);
-        $store->getActivity('test', 10, 0);
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public function testCanDeleteDataOlderThan(): void
-    {
-        $dateTime = new DateTimeImmutable();
-
-        $this->repositoryMock->expects($this->once())
-            ->method('deleteAuthenticationEventsOlderThan')
-            ->with($dateTime);
-
-        $store = new Store(
-            $this->moduleConfigurationStub,
-            $this->loggerMock,
-            null,
-            ModuleConfiguration\ConnectionType::MASTER,
-            $this->factoryStub,
-            $this->repositoryMock
-        );
-
-        $store->deleteDataOlderThan($dateTime);
+        $userId = $store->resolveUserId($this->hashDecoratedState);
+        $store->resolveUserVersionId($userId, $this->hashDecoratedState);
     }
 }
diff --git a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/HashDecoratedStateTest.php b/tests/src/Data/Stores/Accounting/Bases/HashDecoratedStateTest.php
similarity index 88%
rename from tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/HashDecoratedStateTest.php
rename to tests/src/Data/Stores/Accounting/Bases/HashDecoratedStateTest.php
index c08603b..0585513 100644
--- a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/HashDecoratedStateTest.php
+++ b/tests/src/Data/Stores/Accounting/Bases/HashDecoratedStateTest.php
@@ -2,15 +2,15 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\Bases;
 
 use PHPUnit\Framework\MockObject\Stub;
-use SimpleSAML\Module\accounting\Entities\Authentication\Event\State;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\HashDecoratedState;
 use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState;
+use SimpleSAML\Module\accounting\Entities\Authentication\Event\State;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\HashDecoratedState
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState
  * @uses \SimpleSAML\Module\accounting\Helpers\Hash
  * @uses \SimpleSAML\Module\accounting\Helpers\Arr
  * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
diff --git a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawConnectedServiceProviderTest.php b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/RawConnectedServiceTest.php
similarity index 60%
rename from tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawConnectedServiceProviderTest.php
rename to tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/RawConnectedServiceTest.php
index 63c4518..a9dc48e 100644
--- a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RawConnectedServiceProviderTest.php
+++ b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/RawConnectedServiceTest.php
@@ -2,22 +2,22 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal;
 
 use DateTimeImmutable;
 use Doctrine\DBAL\Platforms\AbstractPlatform;
 use PHPUnit\Framework\MockObject\Stub;
-use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\RawConnectedServiceProvider;
 use PHPUnit\Framework\TestCase;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\RawConnectedService;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\TableConstants;
+use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
 use SimpleSAML\Test\Module\accounting\Constants\DateTime;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\RawConnectedServiceProvider
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractRawEntity
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\RawConnectedService
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity
  */
-class RawConnectedServiceProviderTest extends TestCase
+class RawConnectedServiceTest extends TestCase
 {
     protected int $numberOfAuthentications;
     protected string $lastAuthenticationAt;
@@ -45,15 +45,15 @@ class RawConnectedServiceProviderTest extends TestCase
         $this->serviceProviderMetadata = ['sp' => 'metadata'];
         $this->userAttributes = ['user' => 'attribute'];
         $this->rawRow = [
-            TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS =>
+            TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS =>
                 $this->numberOfAuthentications,
-            TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_LAST_AUTHENTICATION_AT =>
+            TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT =>
                 $this->lastAuthenticationAt,
-            TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_FIRST_AUTHENTICATION_AT =>
+            TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT =>
                 $this->firstAuthenticationAt,
-            TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_SP_METADATA =>
+            TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA =>
                 serialize($this->serviceProviderMetadata),
-            TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_USER_ATTRIBUTES =>
+            TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES =>
                 serialize($this->userAttributes),
         ];
         $this->dateTimeFormat = DateTime::DEFAULT_FORMAT;
@@ -65,14 +65,17 @@ class RawConnectedServiceProviderTest extends TestCase
     public function testCanCreateInstance(): void
     {
         $this->assertInstanceOf(
-            RawConnectedServiceProvider::class,
-            new RawConnectedServiceProvider($this->rawRow, $this->abstractPlatformStub)
+            RawConnectedService::class,
+            new RawConnectedService($this->rawRow, $this->abstractPlatformStub)
         );
     }
 
     public function testCanGetProperties(): void
     {
-        $rawConnectedServiceProvider = new RawConnectedServiceProvider($this->rawRow, $this->abstractPlatformStub);
+        $rawConnectedServiceProvider = new RawConnectedService(
+            $this->rawRow,
+            $this->abstractPlatformStub
+        );
 
         $this->assertSame($this->numberOfAuthentications, $rawConnectedServiceProvider->getNumberOfAuthentications());
         $this->assertInstanceOf(DateTimeImmutable::class, $rawConnectedServiceProvider->getLastAuthenticationAt());
@@ -92,80 +95,80 @@ class RawConnectedServiceProviderTest extends TestCase
     public function testThrowsIfColumnNotSet(): void
     {
         $rawRow = $this->rawRow;
-        unset($rawRow[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_USER_ATTRIBUTES]);
+        unset($rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES]);
 
         $this->expectException(UnexpectedValueException::class);
 
-        new RawConnectedServiceProvider($rawRow, $this->abstractPlatformStub);
+        new RawConnectedService($rawRow, $this->abstractPlatformStub);
     }
 
     public function testThrowsIfNumberOfAuthenticationsNotNumeric(): void
     {
         $rawRow = $this->rawRow;
-        $rawRow[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS] = 'a';
+        $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS] = 'a';
 
         $this->expectException(UnexpectedValueException::class);
 
-        new RawConnectedServiceProvider($rawRow, $this->abstractPlatformStub);
+        new RawConnectedService($rawRow, $this->abstractPlatformStub);
     }
 
     public function testThrowsIfLastAuthenticationAtNotString(): void
     {
         $rawRow = $this->rawRow;
-        $rawRow[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_LAST_AUTHENTICATION_AT] = 1;
+        $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_LAST_AUTHENTICATION_AT] = 1;
 
         $this->expectException(UnexpectedValueException::class);
 
-        new RawConnectedServiceProvider($rawRow, $this->abstractPlatformStub);
+        new RawConnectedService($rawRow, $this->abstractPlatformStub);
     }
 
     public function testThrowsIfFirstAuthenticationAtNotString(): void
     {
         $rawRow = $this->rawRow;
-        $rawRow[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_FIRST_AUTHENTICATION_AT] = 1;
+        $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_FIRST_AUTHENTICATION_AT] = 1;
 
         $this->expectException(UnexpectedValueException::class);
 
-        new RawConnectedServiceProvider($rawRow, $this->abstractPlatformStub);
+        new RawConnectedService($rawRow, $this->abstractPlatformStub);
     }
 
     public function testThrowsIfSpMetadataNotString(): void
     {
         $rawRow = $this->rawRow;
-        $rawRow[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_SP_METADATA] = 1;
+        $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] = 1;
 
         $this->expectException(UnexpectedValueException::class);
 
-        new RawConnectedServiceProvider($rawRow, $this->abstractPlatformStub);
+        new RawConnectedService($rawRow, $this->abstractPlatformStub);
     }
 
     public function testThrowsIfUserAttributesNotString(): void
     {
         $rawRow = $this->rawRow;
-        $rawRow[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_USER_ATTRIBUTES] = 1;
+        $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] = 1;
 
         $this->expectException(UnexpectedValueException::class);
 
-        new RawConnectedServiceProvider($rawRow, $this->abstractPlatformStub);
+        new RawConnectedService($rawRow, $this->abstractPlatformStub);
     }
 
     public function testThrowsIfSpMetadataNotValid(): void
     {
         $rawRow = $this->rawRow;
-        $rawRow[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_SP_METADATA] = serialize(1);
+        $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA] = serialize(1);
 
         $this->expectException(UnexpectedValueException::class);
 
-        new RawConnectedServiceProvider($rawRow, $this->abstractPlatformStub);
+        new RawConnectedService($rawRow, $this->abstractPlatformStub);
     }
 
     public function testThrowsIfUserAttributesNotValid(): void
     {
         $rawRow = $this->rawRow;
-        $rawRow[TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_USER_ATTRIBUTES] = serialize(1);
+        $rawRow[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES] = serialize(1);
 
         $this->expectException(UnexpectedValueException::class);
 
-        new RawConnectedServiceProvider($rawRow, $this->abstractPlatformStub);
+        new RawConnectedService($rawRow, $this->abstractPlatformStub);
     }
 }
diff --git a/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/RepositoryTest.php b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/RepositoryTest.php
new file mode 100644
index 0000000..aac9186
--- /dev/null
+++ b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/Store/RepositoryTest.php
@@ -0,0 +1,383 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store;
+
+use DateInterval;
+use DateTimeImmutable;
+use Exception;
+use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\TableConstants
+    as BaseTableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Repository;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator;
+use SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
+use SimpleSAML\Test\Module\accounting\Constants\DateTime;
+
+/**
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Helpers\Filesystem
+ * @uses \SimpleSAML\Module\accounting\ModuleConfiguration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000000CreateIdpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000100CreateIdpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000200CreateSpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000300CreateSpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000400CreateUserTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000500CreateUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000700CreateConnectedServiceTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateSpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateSpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpSpUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
+ */
+class RepositoryTest extends TestCase
+{
+    protected Connection $connection;
+    protected \Doctrine\DBAL\Connection $dbal;
+    /**
+     * @var Stub
+     */
+    protected $loggerStub;
+    protected Migrator $migrator;
+    protected string $dateTimeFormat;
+    protected string $idpEntityId;
+    protected string $idpEntityIdHash;
+    protected string $idpMetadata;
+    protected string $idpMetadataHash;
+    protected string $spEntityId;
+    protected string $spMetadataHash;
+    protected string $userIdentifier;
+    protected string $userIdentifierHash;
+    protected string $userAttributes;
+    protected string $userAttributesHash;
+    protected Repository $repository;
+    protected DateTimeImmutable $createdAt;
+    /**
+     * @var Stub
+     */
+    protected $connectionStub;
+    protected string $spEntityIdHash;
+    protected string $spMetadata;
+    protected string $clientIpAddress;
+    protected string $authenticationProtocolDesignation;
+
+    /**
+     * @throws StoreException
+     * @throws MigrationException
+     */
+    protected function setUp(): void
+    {
+        // For stubbing.
+        $this->connectionStub = $this->createStub(Connection::class);
+        $this->loggerStub = $this->createStub(LoggerInterface::class);
+
+        // For real DB testing.
+        $connectionParameters = ConnectionParameters::DBAL_SQLITE_MEMORY;
+        $this->connection = new Connection($connectionParameters);
+        $this->migrator = new Migrator($this->connection, $this->loggerStub);
+        $moduleConfiguration = new ModuleConfiguration();
+        $migrationsDirectory = $moduleConfiguration->getModuleSourceDirectory() . DIRECTORY_SEPARATOR .
+            'Data' . DIRECTORY_SEPARATOR .
+            'Stores' . DIRECTORY_SEPARATOR .
+            'Accounting' . DIRECTORY_SEPARATOR .
+            'ConnectedServices' . DIRECTORY_SEPARATOR .
+            'DoctrineDbal' . DIRECTORY_SEPARATOR .
+            'Versioned' . DIRECTORY_SEPARATOR .
+            'Store' . DIRECTORY_SEPARATOR .
+            AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME;
+        $namespace = Store::class . '\\' . AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME;
+
+        $this->migrator->runSetup();
+        $this->migrator->runNonImplementedMigrationClasses($migrationsDirectory, $namespace);
+
+        $this->repository = new Repository($this->connection, $this->loggerStub);
+
+        $this->dateTimeFormat = DateTime::DEFAULT_FORMAT;
+
+        $this->idpEntityId = 'idp-entity-id';
+        $this->idpEntityIdHash = 'idp-entity-id-hash';
+
+        $this->idpMetadata = 'idp-metadata';
+        $this->idpMetadataHash = 'idp-metadata-hash';
+
+        $this->spEntityId = 'sp-entity-id';
+        $this->spEntityIdHash = 'sp-entity-id-hash';
+
+        $this->spMetadata = 'sp-metadata';
+        $this->spMetadataHash = 'sp-metadata-hash';
+
+        $this->userIdentifier = 'user-identifier';
+        $this->userIdentifierHash = 'user-identifier-hash';
+
+        $this->userAttributes = 'user-attributes';
+        $this->userAttributesHash = 'user-attributes-hash';
+
+        $this->createdAt = new DateTimeImmutable();
+        $this->clientIpAddress = '123.123.123.123';
+        $this->authenticationProtocolDesignation = Saml2::DESIGNATION;
+    }
+
+    public function testCanCreateInstance(): void
+    {
+        $this->assertInstanceOf(
+            Repository::class,
+            new Repository($this->connection, $this->loggerStub)
+        );
+    }
+
+    /**
+     * @throws StoreException
+     * @throws \Doctrine\DBAL\Exception
+     */
+    public function testCanGetConnectedServices(): void
+    {
+        $this->repository->insertIdp($this->idpEntityId, $this->idpEntityIdHash, $this->createdAt);
+        $idpResult = $this->repository->getIdp($this->idpEntityIdHash)->fetchAssociative();
+        $idpId = (int)$idpResult[BaseTableConstants::TABLE_IDP_COLUMN_NAME_ID];
+        $this->repository->insertIdpVersion($idpId, $this->idpMetadata, $this->idpMetadataHash, $this->createdAt);
+
+        $this->repository->insertSp($this->spEntityId, $this->spEntityIdHash, $this->createdAt);
+        $spResult = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative();
+        $spId = (int)$spResult[BaseTableConstants::TABLE_SP_COLUMN_NAME_ID];
+        $this->repository->insertSpVersion($spId, $this->spMetadata, $this->spMetadataHash, $this->createdAt);
+
+        $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt);
+        $userResult = $this->repository->getUser($this->userIdentifierHash)->fetchAssociative();
+        $userId = (int)$userResult[BaseTableConstants::TABLE_USER_COLUMN_NAME_ID];
+        $this->repository
+            ->insertUserVersion($userId, $this->userAttributes, $this->userAttributesHash, $this->createdAt);
+
+        $idpVersionResult = $this->repository->getIdpVersion($idpId, $this->idpMetadataHash)->fetchAssociative();
+        $spVersionResult = $this->repository->getSpVersion($spId, $this->spMetadataHash)->fetchAssociative();
+        $userVersionResult = $this->repository->getUserVersion($userId, $this->userAttributesHash)->fetchAssociative();
+
+        $idpVersionId = (int)$idpVersionResult[BaseTableConstants::TABLE_IDP_VERSION_COLUMN_NAME_ID];
+        $spVersionId = (int)$spVersionResult[BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID];
+        $userVersionId = (int)$userVersionResult[BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID];
+
+        $this->repository->insertIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId, $this->createdAt);
+        $idpSpUserVersionResult = $this->repository->getIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId)
+            ->fetchAssociative();
+
+        $idpSpUserVersionId =
+            (int)$idpSpUserVersionResult[BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
+
+        $resultArray = $this->repository->getConnectedServices($this->userIdentifierHash);
+
+        $this->assertCount(0, $resultArray);
+
+        $this->repository->insertConnectedService($idpSpUserVersionId);
+
+        $resultArray = $this->repository->getConnectedServices($this->userIdentifierHash);
+        $this->assertCount(1, $resultArray);
+
+        $this->assertEquals(
+            '1',
+            $resultArray[$this->spEntityId]
+            [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS]
+        );
+        $this->assertSame(
+            $this->spMetadata,
+            $resultArray[$this->spEntityId]
+            [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA]
+        );
+        $this->assertSame(
+            $this->userAttributes,
+            $resultArray[$this->spEntityId]
+            [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES]
+        );
+
+        $connectedServiceId = (int)$this->repository->getConnectedService($idpSpUserVersionId)->fetchOne();
+
+        $this->repository->updateConnectedServiceVersionCount(
+            $connectedServiceId,
+            new DateTimeImmutable()
+        );
+
+        $resultArray = $this->repository->getConnectedServices($this->userIdentifierHash);
+        $this->assertCount(1, $resultArray);
+        $this->assertEquals(
+            '2',
+            $resultArray[$this->spEntityId]
+            [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS]
+        );
+        $this->assertSame(
+            $this->spMetadata,
+            $resultArray[$this->spEntityId]
+            [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA]
+        );
+        $this->assertSame(
+            $this->userAttributes,
+            $resultArray[$this->spEntityId]
+            [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES]
+        );
+
+        // Simulate another SP
+        $spEntityIdNew = $this->spEntityId . '-new';
+        $spEntityIdHashNew = $this->spEntityIdHash . '-new';
+        $spMetadataNew = $this->spMetadata . '-new';
+        $spMetadataHashNew = $this->spMetadataHash . '-new';
+        $this->repository->insertSp($spEntityIdNew, $spEntityIdHashNew, $this->createdAt);
+        $spResult = $this->repository->getSp($spEntityIdHashNew)->fetchAssociative();
+        $spId = (int)$spResult[BaseTableConstants::TABLE_SP_COLUMN_NAME_ID];
+        $this->repository->insertSpVersion($spId, $spMetadataNew, $spMetadataHashNew, $this->createdAt);
+        $spVersionResult = $this->repository->getSpVersion($spId, $spMetadataHashNew)->fetchAssociative();
+        $spVersionId = (int)$spVersionResult[BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID];
+
+        $this->repository->insertIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId, $this->createdAt);
+        $idpSpUserVersionResult = $this->repository->getIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId)
+            ->fetchAssociative();
+
+        $idpSpUserVersionId =
+            (int)$idpSpUserVersionResult[BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
+
+        $this->repository->insertConnectedService($idpSpUserVersionId);
+
+        $resultArray = $this->repository->getConnectedServices($this->userIdentifierHash);
+        $this->assertCount(2, $resultArray);
+        $this->assertEquals(
+            '1',
+            $resultArray[$spEntityIdNew]
+            [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS]
+        );
+        $this->assertSame(
+            $spMetadataNew,
+            $resultArray[$spEntityIdNew]
+            [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA]
+        );
+        $this->assertSame(
+            $this->userAttributes,
+            $resultArray[$this->spEntityId]
+            [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES]
+        );
+
+        // Simulate change in user attributes
+        $userAttributesNew = $this->userAttributes . '-new';
+        $userAttributesHashNew = $this->userAttributesHash . '-new';
+        $this->repository->insertUserVersion($userId, $userAttributesNew, $userAttributesHashNew, $this->createdAt);
+        $userVersionResult = $this->repository->getUserVersion($userId, $userAttributesHashNew)->fetchAssociative();
+        $userVersionId = (int)$userVersionResult[BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID];
+        $this->repository->insertIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId);
+        $idpSpUserVersionResult = $this->repository->getIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId)
+            ->fetchAssociative();
+        $idpSpUserVersionId =
+            (int)$idpSpUserVersionResult[BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
+
+        $this->repository->insertConnectedService($idpSpUserVersionId);
+
+        $resultArray = $this->repository->getConnectedServices($this->userIdentifierHash);
+
+        $this->assertCount(2, $resultArray);
+        $this->assertEquals(
+            '2',
+            $resultArray[$spEntityIdNew]
+            [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS]
+        );
+        $this->assertSame(
+            $spMetadataNew,
+            $resultArray[$spEntityIdNew]
+            [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_SP_METADATA]
+        );
+        // New SP with new user attributes version..
+        $this->assertSame(
+            $userAttributesNew,
+            $resultArray[$spEntityIdNew]
+            [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES]
+        );
+
+        // First SP still has old user attributes version...
+        $this->assertSame(
+            $this->userAttributes,
+            $resultArray[$this->spEntityId]
+            [TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_USER_ATTRIBUTES]
+        );
+    }
+
+    public function testGetConnectedServiceProvidersThrowsOnInvalidDbal(): void
+    {
+        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
+        $repository = new Repository($this->connectionStub, $this->loggerStub);
+        $this->expectException(StoreException::class);
+
+        $repository->getConnectedServices($this->userIdentifierHash);
+    }
+
+    /**
+     * @throws StoreException
+     * @throws \Doctrine\DBAL\Exception
+     */
+    public function testCanDeleteConnectedServicesOlderThan(): void
+    {
+        $this->repository->insertIdp($this->idpEntityId, $this->idpEntityIdHash, $this->createdAt);
+        $idpResult = $this->repository->getIdp($this->idpEntityIdHash)->fetchAssociative();
+        $idpId = (int)$idpResult[BaseTableConstants::TABLE_IDP_COLUMN_NAME_ID];
+        $this->repository->insertIdpVersion($idpId, $this->idpMetadata, $this->idpMetadataHash, $this->createdAt);
+
+        $this->repository->insertSp($this->spEntityId, $this->spEntityIdHash, $this->createdAt);
+        $spResult = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative();
+        $spId = (int)$spResult[BaseTableConstants::TABLE_SP_COLUMN_NAME_ID];
+        $this->repository->insertSpVersion($spId, $this->spMetadata, $this->spMetadataHash, $this->createdAt);
+
+        $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt);
+        $userResult = $this->repository->getUser($this->userIdentifierHash)->fetchAssociative();
+        $userId = (int)$userResult[BaseTableConstants::TABLE_USER_COLUMN_NAME_ID];
+        $this->repository
+            ->insertUserVersion($userId, $this->userAttributes, $this->userAttributesHash, $this->createdAt);
+
+        $idpVersionResult = $this->repository->getIdpVersion($idpId, $this->idpMetadataHash)->fetchAssociative();
+        $spVersionResult = $this->repository->getSpVersion($spId, $this->spMetadataHash)->fetchAssociative();
+        $userVersionResult = $this->repository->getUserVersion($userId, $this->userAttributesHash)->fetchAssociative();
+
+        $idpVersionId = (int)$idpVersionResult[BaseTableConstants::TABLE_IDP_VERSION_COLUMN_NAME_ID];
+        $spVersionId = (int)$spVersionResult[BaseTableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID];
+        $userVersionId = (int)$userVersionResult[BaseTableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID];
+
+        $this->repository->insertIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId, $this->createdAt);
+        $idpSpUserVersionResult = $this->repository->getIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId)
+            ->fetchAssociative();
+
+        $idpSpUserVersionId =
+            (int)$idpSpUserVersionResult[BaseTableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
+
+        $this->repository->insertConnectedService($idpSpUserVersionId);
+
+        $resultArray = $this->repository->getConnectedServices($this->userIdentifierHash);
+        $this->assertCount(1, $resultArray);
+
+        $dateTimeInFuture = $this->createdAt->add(new DateInterval('P1D'));
+
+        $this->repository->deleteConnectedServicesOlderThan($dateTimeInFuture);
+
+        $resultArray = $this->repository->getConnectedServices($this->userIdentifierHash);
+        $this->assertCount(0, $resultArray);
+    }
+
+    public function testDeleteAuthenticationEventsOlderThanThrowsOnInvalidDbal(): void
+    {
+        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
+        $repository = new Repository($this->connectionStub, $this->loggerStub);
+        $this->expectException(StoreException::class);
+
+        $repository->deleteConnectedServicesOlderThan(new DateTimeImmutable());
+    }
+}
diff --git a/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/StoreTest.php b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/StoreTest.php
new file mode 100644
index 0000000..3e810e1
--- /dev/null
+++ b/tests/src/Data/Stores/Accounting/ConnectedServices/DoctrineDbal/Versioned/StoreTest.php
@@ -0,0 +1,257 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned;
+
+use DateTimeImmutable;
+use Doctrine\DBAL\Result;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Repository;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\TableConstants;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator;
+use SimpleSAML\Module\accounting\Entities\Authentication\Event;
+use SimpleSAML\Module\accounting\Entities\Authentication\Event\State;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\HelpersManager;
+use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
+use SimpleSAML\Test\Module\accounting\Constants\RawRowResult;
+use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
+
+/**
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event
+ * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractState
+ * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event\State\Saml2
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000000CreateIdpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000100CreateIdpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000200CreateSpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000300CreateSpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000400CreateUserTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000500CreateUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Migrations\Version20220801000700CreateConnectedServiceTable
+ * @uses \SimpleSAML\Module\accounting\Helpers\Filesystem
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\HashDecoratedState
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateSpTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateSpVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Migrations\CreateIdpSpUserVersionTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Helpers\Hash
+ * @uses \SimpleSAML\Module\accounting\Helpers\Arr
+ * @uses \SimpleSAML\Module\accounting\Helpers\Network
+ * @uses \SimpleSAML\Module\accounting\Entities\ConnectedService\Bag
+ * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractProvider
+ * @uses \SimpleSAML\Module\accounting\Entities\ConnectedService
+ * @uses \SimpleSAML\Module\accounting\Entities\User
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\RawConnectedService
+ * @uses \SimpleSAML\Module\accounting\Entities\Activity\Bag
+ * @uses \SimpleSAML\Module\accounting\Entities\Activity
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
+ * @uses \SimpleSAML\Module\accounting\Entities\Providers\Service\Saml2
+ * @uses \SimpleSAML\Module\accounting\Helpers\ProviderResolver
+ * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2
+ */
+class StoreTest extends TestCase
+{
+    protected Stub $moduleConfigurationStub;
+    protected Migrator $migrator;
+    protected Stub $factoryStub;
+    protected Connection $connection;
+    protected State\Saml2 $state;
+    protected Event $authenticationEvent;
+    protected HashDecoratedState $hashDecoratedState;
+    /**
+     * @var MockObject
+     */
+    protected $repositoryMock;
+    /**
+     * @var Stub
+     */
+    protected $resultStub;
+    /**
+     * @var MockObject
+     */
+    protected $loggerMock;
+    /**
+     * @var MockObject
+     */
+    protected $helpersManagerMock;
+
+    /**
+     * @throws StoreException
+     */
+    protected function setUp(): void
+    {
+        $connectionParams = ConnectionParameters::DBAL_SQLITE_MEMORY;
+        $this->moduleConfigurationStub = $this->createStub(ModuleConfiguration::class);
+        $this->moduleConfigurationStub->method('getConnectionParameters')
+            ->willReturn($connectionParams);
+        $this->moduleConfigurationStub->method('getUserIdAttributeName')
+            ->willReturn('hrEduPersonPersistentID');
+
+        $this->connection = new Connection($connectionParams);
+
+        $this->loggerMock = $this->createMock(LoggerInterface::class);
+
+        $this->migrator = new Migrator($this->connection, $this->loggerMock);
+
+        $this->factoryStub = $this->createStub(Factory::class);
+        $this->factoryStub->method('buildConnection')->willReturn($this->connection);
+        $this->factoryStub->method('buildMigrator')->willReturn($this->migrator);
+
+        $this->state = new State\Saml2(StateArrays::SAML2_FULL);
+        $this->authenticationEvent = new Event($this->state);
+
+        $this->hashDecoratedState = new HashDecoratedState($this->state);
+        $this->repositoryMock = $this->createMock(
+            Repository::class
+        );
+
+        $this->resultStub = $this->createStub(Result::class);
+        $this->helpersManagerMock = $this->createMock(HelpersManager::class);
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testCanConstructInstance(): void
+    {
+        $this->assertInstanceOf(
+            Store::class,
+            new Store(
+                $this->moduleConfigurationStub,
+                $this->loggerMock,
+                null,
+                ModuleConfiguration\ConnectionType::MASTER,
+                $this->factoryStub
+            )
+        );
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testCanBuildInstance(): void
+    {
+        $this->assertInstanceOf(
+            Store::class,
+            Store::build($this->moduleConfigurationStub, $this->loggerMock)
+        );
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testGetConnectedOrganizationsReturnsEmptyBagIfNoResults(): void
+    {
+        $this->repositoryMock->method('getConnectedServices')->willReturn([]);
+
+        $store = new Store(
+            $this->moduleConfigurationStub,
+            $this->loggerMock,
+            null,
+            ModuleConfiguration\ConnectionType::MASTER,
+            $this->factoryStub,
+            $this->helpersManagerMock,
+            $this->repositoryMock
+        );
+
+        $connectedServiceProviderBag = $store->getConnectedServices('test');
+
+        $this->assertEmpty($connectedServiceProviderBag->getAll());
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testCanGetConnectedOrganizationsBag(): void
+    {
+        $this->repositoryMock->method('getConnectedServices')
+            ->willReturn([RawRowResult::CONNECTED_SERVICE]);
+
+        $store = new Store(
+            $this->moduleConfigurationStub,
+            $this->loggerMock,
+            null,
+            ModuleConfiguration\ConnectionType::MASTER,
+            $this->factoryStub,
+            $this->helpersManagerMock,
+            $this->repositoryMock
+        );
+
+        $connectedServiceProviderBag = $store->getConnectedServices('test');
+
+        $this->assertNotEmpty($connectedServiceProviderBag->getAll());
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testGetConnectedOrganizationsThrowsForInvalidResult(): void
+    {
+        $rawResult = RawRowResult::CONNECTED_SERVICE;
+        unset($rawResult[TableConstants::ENTITY_CONNECTED_SERVICE_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS]);
+
+        $this->repositoryMock->method('getConnectedServices')
+            ->willReturn([$rawResult]);
+
+        $store = new Store(
+            $this->moduleConfigurationStub,
+            $this->loggerMock,
+            null,
+            ModuleConfiguration\ConnectionType::MASTER,
+            $this->factoryStub,
+            $this->helpersManagerMock,
+            $this->repositoryMock
+        );
+
+        $this->expectException(StoreException::class);
+        $store->getConnectedServices('test');
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testCanDeleteDataOlderThan(): void
+    {
+        $dateTime = new DateTimeImmutable();
+
+        $this->repositoryMock->expects($this->once())
+            ->method('deleteConnectedServicesOlderThan')
+            ->with($dateTime);
+
+        $store = new Store(
+            $this->moduleConfigurationStub,
+            $this->loggerMock,
+            null,
+            ModuleConfiguration\ConnectionType::MASTER,
+            $this->factoryStub,
+            $this->helpersManagerMock,
+            $this->repositoryMock
+        );
+
+        $store->deleteDataOlderThan($dateTime);
+    }
+}
diff --git a/tests/src/Stores/Bases/AbstractStoreTest.php b/tests/src/Data/Stores/Bases/AbstractStoreTest.php
similarity index 72%
rename from tests/src/Stores/Bases/AbstractStoreTest.php
rename to tests/src/Data/Stores/Bases/AbstractStoreTest.php
index 9e45460..99576c2 100644
--- a/tests/src/Stores/Bases/AbstractStoreTest.php
+++ b/tests/src/Data/Stores/Bases/AbstractStoreTest.php
@@ -2,20 +2,20 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Bases;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Bases;
 
 use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Stores\Bases\AbstractStore;
-use PHPUnit\Framework\TestCase;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Bases\AbstractStore
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore
  */
 class AbstractStoreTest extends TestCase
 {
-    protected AbstractStore $abstractStore;
+    protected \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore $abstractStore;
     /**
      * @var Stub
      */
@@ -33,12 +33,12 @@ class AbstractStoreTest extends TestCase
         $this->abstractStore = new class (
             $this->moduleConfigurationStub,
             $this->loggerStub
-        ) extends AbstractStore {
+        ) extends \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore {
             public static function build(
                 ModuleConfiguration $moduleConfiguration,
                 LoggerInterface $logger,
                 string $connectionKey = null
-            ): AbstractStore {
+            ): \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore {
                 return new self($moduleConfiguration, $logger, $connectionKey);
             }
             public function needsSetup(): bool
@@ -55,7 +55,7 @@ class AbstractStoreTest extends TestCase
     {
         $this->assertInstanceOf(AbstractStore::class, $this->abstractStore);
         $this->assertInstanceOf(
-            AbstractStore::class,
+            \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore::class,
             $this->abstractStore::build($this->moduleConfigurationStub, $this->loggerStub)
         );
     }
diff --git a/tests/src/Stores/Bases/DoctrineDbal/AbstractRawEntityTest.php b/tests/src/Data/Stores/Bases/DoctrineDbal/AbstractRawEntityTest.php
similarity index 89%
rename from tests/src/Stores/Bases/DoctrineDbal/AbstractRawEntityTest.php
rename to tests/src/Data/Stores/Bases/DoctrineDbal/AbstractRawEntityTest.php
index 4c1fff1..60f26c9 100644
--- a/tests/src/Stores/Bases/DoctrineDbal/AbstractRawEntityTest.php
+++ b/tests/src/Data/Stores/Bases/DoctrineDbal/AbstractRawEntityTest.php
@@ -2,17 +2,17 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Bases\DoctrineDbal;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Bases\DoctrineDbal;
 
 use Doctrine\DBAL\Platforms\AbstractPlatform;
 use PHPUnit\Framework\MockObject\Stub;
-use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
-use SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractRawEntity;
 use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity;
+use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
 use SimpleSAML\Test\Module\accounting\Constants\DateTime;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractRawEntity
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity
  */
 class AbstractRawEntityTest extends TestCase
 {
diff --git a/tests/src/Stores/Builders/DataStoreBuilderTest.php b/tests/src/Data/Stores/Builders/DataStoreBuilderTest.php
similarity index 55%
rename from tests/src/Stores/Builders/DataStoreBuilderTest.php
rename to tests/src/Data/Stores/Builders/DataStoreBuilderTest.php
index 307a5a4..91ed81c 100644
--- a/tests/src/Stores/Builders/DataStoreBuilderTest.php
+++ b/tests/src/Data/Stores/Builders/DataStoreBuilderTest.php
@@ -2,31 +2,34 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Builders;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Builders;
 
 use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store;
+use SimpleSAML\Module\accounting\Data\Stores\Builders\DataStoreBuilder;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\HelpersManager;
-use SimpleSAML\Module\accounting\Stores\Builders\DataStoreBuilder;
-use PHPUnit\Framework\TestCase;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Builders\DataStoreBuilder
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Builders\DataStoreBuilder
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store
  * @uses \SimpleSAML\Module\accounting\Helpers\InstanceBuilderUsingModuleConfiguration
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractStore
- * @uses \SimpleSAML\Module\accounting\Stores\Builders\Bases\AbstractStoreBuilder
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Repository
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Builders\Bases\AbstractStoreBuilder
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator
  * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Providers\Activity\DoctrineDbal\VersionedDataProvider
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository
  */
 class DataStoreBuilderTest extends TestCase
 {
diff --git a/tests/src/Stores/Builders/JobsStoreBuilderTest.php b/tests/src/Data/Stores/Builders/JobsStoreBuilderTest.php
similarity index 77%
rename from tests/src/Stores/Builders/JobsStoreBuilderTest.php
rename to tests/src/Data/Stores/Builders/JobsStoreBuilderTest.php
index cb1add5..6890293 100644
--- a/tests/src/Stores/Builders/JobsStoreBuilderTest.php
+++ b/tests/src/Data/Stores/Builders/JobsStoreBuilderTest.php
@@ -2,33 +2,33 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Builders;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Builders;
 
 use PHPUnit\Framework\MockObject\Stub;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Builders\Bases\AbstractStoreBuilder;
+use SimpleSAML\Module\accounting\Data\Stores\Builders\JobsStoreBuilder;
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\StoreInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\HelpersManager;
-use SimpleSAML\Module\accounting\Stores\Builders\Bases\AbstractStoreBuilder;
-use SimpleSAML\Module\accounting\Stores\Builders\JobsStoreBuilder;
-use SimpleSAML\Module\accounting\Stores\Interfaces\StoreInterface;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Builders\Bases\AbstractStoreBuilder
- * @covers \SimpleSAML\Module\accounting\Stores\Builders\JobsStoreBuilder
- * @uses   \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Repository
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractStore
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Builders\Bases\AbstractStoreBuilder
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Builders\JobsStoreBuilder
+ * @uses   \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore
  * @uses \SimpleSAML\Module\accounting\Helpers\InstanceBuilderUsingModuleConfiguration
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator
  * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore
  */
 class JobsStoreBuilderTest extends TestCase
 {
diff --git a/tests/src/Stores/Connections/Bases/AbstractMigratorTest.php b/tests/src/Data/Stores/Connections/Bases/AbstractMigratorTest.php
similarity index 76%
rename from tests/src/Stores/Connections/Bases/AbstractMigratorTest.php
rename to tests/src/Data/Stores/Connections/Bases/AbstractMigratorTest.php
index 8a932f4..9b8d3e4 100644
--- a/tests/src/Stores/Connections/Bases/AbstractMigratorTest.php
+++ b/tests/src/Data/Stores/Connections/Bases/AbstractMigratorTest.php
@@ -2,33 +2,35 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Connections\Bases;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Connections\Bases;
 
 use Doctrine\DBAL\Schema\AbstractSchemaManager;
 use Exception;
 use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000000CreateJobTable;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\TableConstants;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\Logger;
-use SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator;
-use PHPUnit\Framework\TestCase;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator
  * @uses \SimpleSAML\Module\accounting\Helpers\Filesystem
  * @uses \SimpleSAML\Module\accounting\ModuleConfiguration
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000000CreateJobTable
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000100CreateJobFailedTable
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Bases\AbstractCreateJobsTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000000CreateJobTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000100CreateJobFailedTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Bases\AbstractCreateJobsTable
  * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class AbstractMigratorTest extends TestCase
@@ -89,7 +91,9 @@ class AbstractMigratorTest extends TestCase
 
         $migrationClasses = $migrator->gatherMigrationClassesFromDirectory($directory, $namespace);
 
-        $jobsTableName = $this->connection->preparePrefixedTableName(Store\TableConstants::TABLE_NAME_JOB);
+        $jobsTableName = $this->connection->preparePrefixedTableName(
+            TableConstants::TABLE_NAME_JOB
+        );
 
         $this->assertFalse($this->schemaManager->tablesExist($jobsTableName));
 
@@ -150,7 +154,7 @@ class AbstractMigratorTest extends TestCase
         );
 
         $this->assertTrue(in_array(
-            Store\Migrations\Version20220601000000CreateJobTable::class,
+            Version20220601000000CreateJobTable::class,
             $nonImplementedMigrationClasses
         ));
     }
@@ -193,8 +197,12 @@ class AbstractMigratorTest extends TestCase
     protected function getSampleMigrationsDirectory(): string
     {
         return $this->moduleConfiguration->getModuleSourceDirectory() . DIRECTORY_SEPARATOR .
-            'Stores' . DIRECTORY_SEPARATOR . 'Jobs' . DIRECTORY_SEPARATOR . 'DoctrineDbal' . DIRECTORY_SEPARATOR .
-            'Store' . DIRECTORY_SEPARATOR . AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME;
+            'Data' . DIRECTORY_SEPARATOR .
+            'Stores' . DIRECTORY_SEPARATOR .
+            'Jobs' . DIRECTORY_SEPARATOR .
+            'DoctrineDbal' . DIRECTORY_SEPARATOR .
+            'Store' . DIRECTORY_SEPARATOR .
+            AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME;
     }
 
     protected function getSampleNameSpace(): string
diff --git a/tests/src/Stores/Connections/DoctrineDbal/Bases/AbstractMigrationTest.php b/tests/src/Data/Stores/Connections/DoctrineDbal/Bases/AbstractMigrationTest.php
similarity index 83%
rename from tests/src/Stores/Connections/DoctrineDbal/Bases/AbstractMigrationTest.php
rename to tests/src/Data/Stores/Connections/DoctrineDbal/Bases/AbstractMigrationTest.php
index 8709e6e..c072470 100644
--- a/tests/src/Stores/Connections/DoctrineDbal/Bases/AbstractMigrationTest.php
+++ b/tests/src/Data/Stores/Connections/DoctrineDbal/Bases/AbstractMigrationTest.php
@@ -2,20 +2,20 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Connections\DoctrineDbal\Bases;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases;
 
 use Exception;
-use SimpleSAML\Module\accounting\Exceptions\StoreException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
 use PHPUnit\Framework\TestCase;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000000CreateJobTable;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000000CreateJobTable;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000000CreateJobTable
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000000CreateJobTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
  */
 class AbstractMigrationTest extends TestCase
 {
diff --git a/tests/src/Stores/Connections/DoctrineDbal/ConnectionTest.php b/tests/src/Data/Stores/Connections/DoctrineDbal/ConnectionTest.php
similarity index 79%
rename from tests/src/Stores/Connections/DoctrineDbal/ConnectionTest.php
rename to tests/src/Data/Stores/Connections/DoctrineDbal/ConnectionTest.php
index 98ff0dd..a47f7de 100644
--- a/tests/src/Stores/Connections/DoctrineDbal/ConnectionTest.php
+++ b/tests/src/Data/Stores/Connections/DoctrineDbal/ConnectionTest.php
@@ -2,14 +2,14 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Connections\DoctrineDbal;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Connections\DoctrineDbal;
 
 use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
 use SimpleSAML\Module\accounting\Exceptions\InvalidConfigurationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
  */
 class ConnectionTest extends TestCase
 {
@@ -52,6 +52,6 @@ class ConnectionTest extends TestCase
         $parameters['table_prefix'] = new class () {
         };
 
-        (new Connection($parameters));
+        (new \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection($parameters));
     }
 }
diff --git a/tests/src/Stores/Connections/DoctrineDbal/FactoryTest.php b/tests/src/Data/Stores/Connections/DoctrineDbal/FactoryTest.php
similarity index 69%
rename from tests/src/Stores/Connections/DoctrineDbal/FactoryTest.php
rename to tests/src/Data/Stores/Connections/DoctrineDbal/FactoryTest.php
index d1cd3d0..76c6374 100644
--- a/tests/src/Stores/Connections/DoctrineDbal/FactoryTest.php
+++ b/tests/src/Data/Stores/Connections/DoctrineDbal/FactoryTest.php
@@ -2,24 +2,24 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Connections\DoctrineDbal;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Connections\DoctrineDbal;
 
 use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\Logger;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory;
-use PHPUnit\Framework\TestCase;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory
  * @uses \SimpleSAML\Module\accounting\ModuleConfiguration
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator
  * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class FactoryTest extends TestCase
diff --git a/tests/src/Stores/Connections/DoctrineDbal/MigratorTest.php b/tests/src/Data/Stores/Connections/DoctrineDbal/MigratorTest.php
similarity index 82%
rename from tests/src/Stores/Connections/DoctrineDbal/MigratorTest.php
rename to tests/src/Data/Stores/Connections/DoctrineDbal/MigratorTest.php
index a7d4496..1304a58 100644
--- a/tests/src/Stores/Connections/DoctrineDbal/MigratorTest.php
+++ b/tests/src/Data/Stores/Connections/DoctrineDbal/MigratorTest.php
@@ -2,37 +2,38 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Connections\DoctrineDbal;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Connections\DoctrineDbal;
 
 use Doctrine\DBAL\Exception;
 use Doctrine\DBAL\Query\QueryBuilder;
+use Doctrine\DBAL\Schema\AbstractSchemaManager;
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
-use Doctrine\DBAL\Schema\AbstractSchemaManager;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator;
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\MigrationInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\TableConstants;
 use SimpleSAML\Module\accounting\Exceptions\InvalidValueException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\Logger;
-use SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use SimpleSAML\Module\accounting\Stores\Interfaces\MigrationInterface;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
 use function PHPUnit\Framework\assertFalse;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator
- * @covers \SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000000CreateJobTable
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000100CreateJobFailedTable
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000000CreateJobTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000100CreateJobFailedTable
  * @uses \SimpleSAML\Module\accounting\ModuleConfiguration
  * @uses \SimpleSAML\Module\accounting\Helpers\Filesystem
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Bases\AbstractCreateJobsTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Bases\AbstractCreateJobsTable
  * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
 class MigratorTest extends TestCase
@@ -108,7 +109,9 @@ class MigratorTest extends TestCase
 
         $migrator->runSetup();
 
-        $tableNameJobs = $this->connection->preparePrefixedTableName(Store\TableConstants::TABLE_NAME_JOB);
+        $tableNameJobs = $this->connection->preparePrefixedTableName(
+            TableConstants::TABLE_NAME_JOB
+        );
         $this > assertFalse($this->schemaManager->tablesExist($tableNameJobs));
 
         $migrator->runMigrationClasses([Store\Migrations\Version20220601000000CreateJobTable::class]);
@@ -202,7 +205,9 @@ class MigratorTest extends TestCase
             ->willReturn(false);
         $dbalStub = $this->createStub(\Doctrine\DBAL\Connection::class);
         $dbalStub->method('createSchemaManager')->willReturn($schemaManagerStub);
-        $connectionStub = $this->createStub(Connection::class);
+        $connectionStub = $this->createStub(
+            Connection::class
+        );
         $connectionStub->method('dbal')->willReturn($dbalStub);
 
         $migrator = new Migrator($connectionStub, $this->loggerServiceMock);
@@ -223,7 +228,9 @@ class MigratorTest extends TestCase
             ->willThrowException(new Exception('test'));
         $dbalStub = $this->createStub(\Doctrine\DBAL\Connection::class);
         $dbalStub->method('createQueryBuilder')->willReturn($queryBuilderStub);
-        $connectionStub = $this->createStub(Connection::class);
+        $connectionStub = $this->createStub(
+            Connection::class
+        );
         $connectionStub->method('dbal')->willReturn($dbalStub);
         $connectionStub->method('preparePrefixedTableName')->willReturn(Migrator::TABLE_NAME);
 
@@ -252,8 +259,12 @@ class MigratorTest extends TestCase
     protected function getSampleMigrationsDirectory(): string
     {
         return $this->moduleConfiguration->getModuleSourceDirectory() . DIRECTORY_SEPARATOR .
-            'Stores' . DIRECTORY_SEPARATOR . 'Jobs' . DIRECTORY_SEPARATOR . 'DoctrineDbal' . DIRECTORY_SEPARATOR .
-            'Store' . DIRECTORY_SEPARATOR . AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME;
+            'Data' . DIRECTORY_SEPARATOR .
+            'Stores' . DIRECTORY_SEPARATOR .
+            'Jobs' . DIRECTORY_SEPARATOR .
+            'DoctrineDbal' . DIRECTORY_SEPARATOR .
+            'Store' . DIRECTORY_SEPARATOR .
+            AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME;
     }
 
     protected function getSampleNameSpace(): string
diff --git a/tests/src/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTableTest.php b/tests/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTableTest.php
similarity index 77%
rename from tests/src/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTableTest.php
rename to tests/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTableTest.php
index c346d8b..ec24501 100644
--- a/tests/src/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTableTest.php
+++ b/tests/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000000CreateJobTableTest.php
@@ -2,23 +2,23 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations;
 
 use Doctrine\DBAL\Exception;
 use Doctrine\DBAL\Schema\AbstractSchemaManager;
+use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000000CreateJobTable;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\TableConstants;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000000CreateJobTable;
-use PHPUnit\Framework\TestCase;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000000CreateJobTable
- * @covers \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Bases\AbstractCreateJobsTable
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000000CreateJobTable
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Bases\AbstractCreateJobsTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
  */
 class Version20220601000000CreateJobTableTest extends TestCase
 {
@@ -33,7 +33,9 @@ class Version20220601000000CreateJobTableTest extends TestCase
     {
         $this->connection = new Connection(ConnectionParameters::DBAL_SQLITE_MEMORY);
         $this->schemaManager = $this->connection->dbal()->createSchemaManager();
-        $this->tableName = $this->connection->preparePrefixedTableName(Store\TableConstants::TABLE_NAME_JOB);
+        $this->tableName = $this->connection->preparePrefixedTableName(
+            TableConstants::TABLE_NAME_JOB
+        );
     }
 
     /**
diff --git a/tests/src/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTableTest.php b/tests/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTableTest.php
similarity index 78%
rename from tests/src/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTableTest.php
rename to tests/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTableTest.php
index f4fcabd..ea7658c 100644
--- a/tests/src/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTableTest.php
+++ b/tests/src/Data/Stores/Jobs/DoctrineDbal/Store/Migrations/Version20220601000100CreateJobFailedTableTest.php
@@ -2,23 +2,23 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations;
 
 use Doctrine\DBAL\Exception;
 use Doctrine\DBAL\Schema\AbstractSchemaManager;
+use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\TableConstants;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations;
-use PHPUnit\Framework\TestCase;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000100CreateJobFailedTable
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Bases\AbstractCreateJobsTable
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000100CreateJobFailedTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Bases\AbstractCreateJobsTable
  */
 class Version20220601000100CreateJobFailedTableTest extends TestCase
 {
@@ -33,7 +33,9 @@ class Version20220601000100CreateJobFailedTableTest extends TestCase
     {
         $this->connection = new Connection(ConnectionParameters::DBAL_SQLITE_MEMORY);
         $this->schemaManager = $this->connection->dbal()->createSchemaManager();
-        $this->tableName = $this->connection->preparePrefixedTableName(Store\TableConstants::TABLE_NAME_JOB_FAILED);
+        $this->tableName = $this->connection->preparePrefixedTableName(
+            TableConstants::TABLE_NAME_JOB_FAILED
+        );
     }
 
     /**
diff --git a/tests/src/Stores/Jobs/DoctrineDbal/Store/RawJobTest.php b/tests/src/Data/Stores/Jobs/DoctrineDbal/Store/RawJobTest.php
similarity index 66%
rename from tests/src/Stores/Jobs/DoctrineDbal/Store/RawJobTest.php
rename to tests/src/Data/Stores/Jobs/DoctrineDbal/Store/RawJobTest.php
index e272b82..31a9685 100644
--- a/tests/src/Stores/Jobs/DoctrineDbal/Store/RawJobTest.php
+++ b/tests/src/Data/Stores/Jobs/DoctrineDbal/Store/RawJobTest.php
@@ -2,26 +2,26 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store;
 
 use DateTimeImmutable;
 use Doctrine\DBAL\Platforms\AbstractPlatform;
 use Doctrine\DBAL\Platforms\SqlitePlatform;
 use PHPUnit\Framework\MockObject\Stub;
 use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\RawJob;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\TableConstants;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event\State;
 use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\RawJob;
 use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\RawJob
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\RawJob
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event
  * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractState
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event\State\Saml2
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractRawEntity
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity
  * @uses \SimpleSAML\Module\accounting\Helpers\Network
  * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
@@ -40,27 +40,27 @@ class RawJobTest extends TestCase
 
         $this->authenticationEvent = new Event(new State\Saml2(StateArrays::SAML2_FULL));
         $this->validRawRow = [
-            Store\TableConstants::COLUMN_NAME_ID => 1,
-            Store\TableConstants::COLUMN_NAME_PAYLOAD => serialize($this->authenticationEvent),
-            Store\TableConstants::COLUMN_NAME_TYPE => get_class($this->authenticationEvent),
-            Store\TableConstants::COLUMN_NAME_CREATED_AT => '2022-08-17 13:26:12',
+            TableConstants::COLUMN_NAME_ID => 1,
+            TableConstants::COLUMN_NAME_PAYLOAD => serialize($this->authenticationEvent),
+            TableConstants::COLUMN_NAME_TYPE => get_class($this->authenticationEvent),
+            TableConstants::COLUMN_NAME_CREATED_AT => '2022-08-17 13:26:12',
         ];
     }
 
     public function testCanInstantiateValidRawJob(): void
     {
         $abstractPlatform = new SqlitePlatform();
-        $rawJob = new Store\RawJob($this->validRawRow, $abstractPlatform);
-        $this->assertSame($rawJob->getId(), $this->validRawRow[Store\TableConstants::COLUMN_NAME_ID]);
+        $rawJob = new RawJob($this->validRawRow, $abstractPlatform);
+        $this->assertSame($rawJob->getId(), $this->validRawRow[TableConstants::COLUMN_NAME_ID]);
         $this->assertEquals($rawJob->getPayload(), $this->authenticationEvent);
-        $this->assertSame($rawJob->getType(), $this->validRawRow[Store\TableConstants::COLUMN_NAME_TYPE]);
+        $this->assertSame($rawJob->getType(), $this->validRawRow[TableConstants::COLUMN_NAME_TYPE]);
         $this->assertInstanceOf(DateTimeImmutable::class, $rawJob->getCreatedAt());
     }
 
     public function testThrowsOnEmptyColumn(): void
     {
         $invalidRawRow = $this->validRawRow;
-        unset($invalidRawRow[Store\TableConstants::COLUMN_NAME_ID]);
+        unset($invalidRawRow[TableConstants::COLUMN_NAME_ID]);
 
         $this->expectException(UnexpectedValueException::class);
 
@@ -70,7 +70,7 @@ class RawJobTest extends TestCase
     public function testThrowsOnNonNumericId(): void
     {
         $invalidRawRow = $this->validRawRow;
-        $invalidRawRow[Store\TableConstants::COLUMN_NAME_ID] = 'a';
+        $invalidRawRow[TableConstants::COLUMN_NAME_ID] = 'a';
 
         $this->expectException(UnexpectedValueException::class);
 
@@ -80,7 +80,7 @@ class RawJobTest extends TestCase
     public function testThrowsOnNonStringPayload(): void
     {
         $invalidRawRow = $this->validRawRow;
-        $invalidRawRow[Store\TableConstants::COLUMN_NAME_PAYLOAD] = 123;
+        $invalidRawRow[TableConstants::COLUMN_NAME_PAYLOAD] = 123;
 
         $this->expectException(UnexpectedValueException::class);
 
@@ -90,27 +90,27 @@ class RawJobTest extends TestCase
     public function testThrowsOnNonAbstractPayload(): void
     {
         $invalidRawRow = $this->validRawRow;
-        $invalidRawRow[Store\TableConstants::COLUMN_NAME_PAYLOAD] = serialize('abc');
+        $invalidRawRow[TableConstants::COLUMN_NAME_PAYLOAD] = serialize('abc');
 
         $this->expectException(UnexpectedValueException::class);
 
-        new Store\RawJob($invalidRawRow, $this->abstractPlatformStub);
+        new RawJob($invalidRawRow, $this->abstractPlatformStub);
     }
 
     public function testThrowsOnNonStringType(): void
     {
         $invalidRawRow = $this->validRawRow;
-        $invalidRawRow[Store\TableConstants::COLUMN_NAME_TYPE] = 123;
+        $invalidRawRow[TableConstants::COLUMN_NAME_TYPE] = 123;
 
         $this->expectException(UnexpectedValueException::class);
 
-        new Store\RawJob($invalidRawRow, $this->abstractPlatformStub);
+        new RawJob($invalidRawRow, $this->abstractPlatformStub);
     }
 
     public function testThrowsOnNonStringCreatedAt(): void
     {
         $invalidRawRow = $this->validRawRow;
-        $invalidRawRow[Store\TableConstants::COLUMN_NAME_CREATED_AT] = 123;
+        $invalidRawRow[TableConstants::COLUMN_NAME_CREATED_AT] = 123;
 
         $this->expectException(UnexpectedValueException::class);
 
@@ -120,10 +120,10 @@ class RawJobTest extends TestCase
     public function testThrowsOnNonValidCreatedAt(): void
     {
         $invalidRawRow = $this->validRawRow;
-        $invalidRawRow[Store\TableConstants::COLUMN_NAME_CREATED_AT] = '123';
+        $invalidRawRow[TableConstants::COLUMN_NAME_CREATED_AT] = '123';
 
         $this->expectException(UnexpectedValueException::class);
 
-        new Store\RawJob($invalidRawRow, $this->abstractPlatformStub);
+        new RawJob($invalidRawRow, $this->abstractPlatformStub);
     }
 }
diff --git a/tests/src/Stores/Jobs/DoctrineDbal/Store/RepositoryTest.php b/tests/src/Data/Stores/Jobs/DoctrineDbal/Store/RepositoryTest.php
similarity index 81%
rename from tests/src/Stores/Jobs/DoctrineDbal/Store/RepositoryTest.php
rename to tests/src/Data/Stores/Jobs/DoctrineDbal/Store/RepositoryTest.php
index 3924415..3b5da38 100644
--- a/tests/src/Stores/Jobs/DoctrineDbal/Store/RepositoryTest.php
+++ b/tests/src/Data/Stores/Jobs/DoctrineDbal/Store/RepositoryTest.php
@@ -2,12 +2,18 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store;
 
 use DateTimeImmutable;
 use Exception;
 use PHPUnit\Framework\MockObject\Stub;
 use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Repository;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\TableConstants;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event\State;
 use SimpleSAML\Module\accounting\Entities\Bases\AbstractJob;
@@ -17,37 +23,32 @@ use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\Logger;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Repository;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Repository
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
  * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractJob
  * @uses \SimpleSAML\Module\accounting\Helpers\Filesystem
  * @uses \SimpleSAML\Module\accounting\ModuleConfiguration
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000000CreateJobTable
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000100CreateJobFailedTable
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\RawJob
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000000CreateJobTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000100CreateJobFailedTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\RawJob
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event\Job
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Bases\AbstractCreateJobsTable
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Bases\AbstractCreateJobsTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore
  * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractState
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event\State\Saml2
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractRawEntity
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity
  * @uses \SimpleSAML\Module\accounting\Helpers\Network
  * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore
  */
 class RepositoryTest extends TestCase
 {
@@ -92,7 +93,9 @@ class RepositoryTest extends TestCase
             $this->factoryStub
         );
 
-        $this->jobsTableName = $this->connection->preparePrefixedTableName(Store\TableConstants::TABLE_NAME_JOB);
+        $this->jobsTableName = $this->connection->preparePrefixedTableName(
+            TableConstants::TABLE_NAME_JOB
+        );
     }
 
     /**
@@ -138,7 +141,7 @@ class RepositoryTest extends TestCase
 
         $this->expectException(StoreException::class);
 
-        $invalidType = str_pad('abc', Store\TableConstants::COLUMN_TYPE_LENGTH + 1);
+        $invalidType = str_pad('abc', TableConstants::COLUMN_TYPE_LENGTH + 1);
         $jobStub = $this->createStub(GenericJob::class);
         $jobStub->method('getPayload')->willReturn($this->payloadStub);
         $jobStub->method('getType')->willReturn($invalidType);
@@ -172,7 +175,7 @@ class RepositoryTest extends TestCase
         $this->jobsStore->runSetup();
 
         $payloadStub = $this->createStub(AbstractPayload::class);
-        $jobStub = $this->createStub(AbstractJob::class); // Abstract classes can't be initialized..
+        $jobStub = $this->createStub(AbstractJob::class); // Abstract classes can't be initialized...
         $jobStub->method('getPayload')->willReturn($payloadStub);
         $jobStub->method('getType')->willReturn(AbstractJob::class);
         $jobStub->method('getCreatedAt')->willReturn(new DateTimeImmutable());
diff --git a/tests/src/Stores/Jobs/DoctrineDbal/StoreTest.php b/tests/src/Data/Stores/Jobs/DoctrineDbal/StoreTest.php
similarity index 86%
rename from tests/src/Stores/Jobs/DoctrineDbal/StoreTest.php
rename to tests/src/Data/Stores/Jobs/DoctrineDbal/StoreTest.php
index d93c787..7e8712d 100644
--- a/tests/src/Stores/Jobs/DoctrineDbal/StoreTest.php
+++ b/tests/src/Data/Stores/Jobs/DoctrineDbal/StoreTest.php
@@ -2,12 +2,18 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Jobs\DoctrineDbal;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Jobs\DoctrineDbal;
 
 use DateTimeImmutable;
 use Doctrine\DBAL\Exception;
 use PHPUnit\Framework\MockObject\Stub;
 use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory;
+use SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Repository;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\TableConstants;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event\State;
 use SimpleSAML\Module\accounting\Entities\Bases\AbstractJob;
@@ -17,37 +23,33 @@ use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\Logger;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store
- * @covers \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractStore
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore
  * @uses \SimpleSAML\Module\accounting\ModuleConfiguration
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator
  * @uses \SimpleSAML\Module\accounting\Helpers\Filesystem
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000000CreateJobTable
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000100CreateJobFailedTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000000CreateJobTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Version20220601000100CreateJobFailedTable
  * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractJob
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\RawJob
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\RawJob
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event\Job
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Repository
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\Store\Migrations\Bases\AbstractCreateJobsTable
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store\Migrations\Bases\AbstractCreateJobsTable
  * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractState
  * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Event\State\Saml2
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractRawEntity
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractRawEntity
  * @uses \SimpleSAML\Module\accounting\Helpers\Network
  * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore
  */
 class StoreTest extends TestCase
 {
@@ -143,9 +145,11 @@ class StoreTest extends TestCase
             $this->factoryStub
         );
 
-        $tableNameJobs = $this->connection->preparePrefixedTableName(Store\TableConstants::TABLE_NAME_JOB);
+        $tableNameJobs = $this->connection->preparePrefixedTableName(
+            TableConstants::TABLE_NAME_JOB
+        );
         $tableNameFailedJobs = $this->connection->preparePrefixedTableName(
-            Store\TableConstants::TABLE_NAME_JOB_FAILED
+            TableConstants::TABLE_NAME_JOB_FAILED
         );
 
         $this->assertSame($tableNameJobs, $jobsStore->getPrefixedTableNameJobs());
@@ -318,7 +322,7 @@ class StoreTest extends TestCase
      */
     public function testDequeueThrowsForJobWithInvalidId(): void
     {
-        $repositoryStub = $this->createStub(Store\Repository::class);
+        $repositoryStub = $this->createStub(Repository::class);
         $jobStub = $this->createStub(GenericJob::class);
         $jobStub->method('getPayload')->willReturn($this->payloadStub);
         $jobStub->method('getCreatedAt')->willReturn(new DateTimeImmutable());
@@ -349,7 +353,7 @@ class StoreTest extends TestCase
      */
     public function testDequeThrowsAfterMaxDeleteAttempts(): void
     {
-        $repositoryStub = $this->createStub(Store\Repository::class);
+        $repositoryStub = $this->createStub(Repository::class);
         $repositoryStub->method('getNext')->willReturn($this->jobStub);
         $repositoryStub->method('delete')->willReturn(false);
 
@@ -375,7 +379,7 @@ class StoreTest extends TestCase
      */
     public function testCanContinueSearchingInCaseOfJobDeletion(): void
     {
-        $repositoryStub = $this->createStub(Store\Repository::class);
+        $repositoryStub = $this->createStub(Repository::class);
         $repositoryStub->method('getNext')->willReturn($this->jobStub);
         $repositoryStub->method('delete')->willReturnOnConsecutiveCalls(false, true);
 
diff --git a/tests/src/Stores/Jobs/PhpRedis/RedisStoreTest.php b/tests/src/Data/Stores/Jobs/PhpRedis/RedisStoreTest.php
similarity index 90%
rename from tests/src/Stores/Jobs/PhpRedis/RedisStoreTest.php
rename to tests/src/Data/Stores/Jobs/PhpRedis/RedisStoreTest.php
index 188a36a..7fce3c2 100644
--- a/tests/src/Stores/Jobs/PhpRedis/RedisStoreTest.php
+++ b/tests/src/Data/Stores/Jobs/PhpRedis/RedisStoreTest.php
@@ -4,24 +4,24 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Jobs\PhpRedis;
+namespace SimpleSAML\Test\Module\accounting\Data\Stores\Jobs\PhpRedis;
 
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 use Redis;
 use RedisException;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\PhpRedis\RedisStore;
 use SimpleSAML\Module\accounting\Entities\GenericJob;
 use SimpleSAML\Module\accounting\Entities\Interfaces\JobInterface;
 use SimpleSAML\Module\accounting\Exceptions\InvalidConfigurationException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Stores\Jobs\PhpRedis\RedisStore;
-use PHPUnit\Framework\TestCase;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Jobs\PhpRedis\RedisStore
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\AbstractStore
+ * @covers \SimpleSAML\Module\accounting\Data\Stores\Jobs\PhpRedis\RedisStore
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore
  */
 class RedisStoreTest extends TestCase
 {
@@ -61,7 +61,7 @@ class RedisStoreTest extends TestCase
 
         $this->assertInstanceOf(
             RedisStore::class,
-            new RedisStore(
+            new \SimpleSAML\Module\accounting\Data\Stores\Jobs\PhpRedis\RedisStore(
                 $this->moduleConfigurationStub,
                 $this->loggerMock,
                 null,
@@ -80,7 +80,7 @@ class RedisStoreTest extends TestCase
 
         $this->assertInstanceOf(
             RedisStore::class,
-            new RedisStore(
+            new \SimpleSAML\Module\accounting\Data\Stores\Jobs\PhpRedis\RedisStore(
                 $this->moduleConfigurationStub,
                 $this->loggerMock,
                 null,
@@ -104,8 +104,8 @@ class RedisStoreTest extends TestCase
         $this->redisMock->method('connect')->willThrowException(new RedisException('test'));
 
         $this->assertInstanceOf(
-            RedisStore::class,
-            new RedisStore(
+            \SimpleSAML\Module\accounting\Data\Stores\Jobs\PhpRedis\RedisStore::class,
+            new \SimpleSAML\Module\accounting\Data\Stores\Jobs\PhpRedis\RedisStore(
                 $this->moduleConfigurationStub,
                 $this->loggerMock,
                 null,
@@ -129,8 +129,8 @@ class RedisStoreTest extends TestCase
         $this->redisMock->method('auth')->willThrowException(new RedisException('test'));
 
         $this->assertInstanceOf(
-            RedisStore::class,
-            new RedisStore(
+            \SimpleSAML\Module\accounting\Data\Stores\Jobs\PhpRedis\RedisStore::class,
+            new \SimpleSAML\Module\accounting\Data\Stores\Jobs\PhpRedis\RedisStore(
                 $this->moduleConfigurationStub,
                 $this->loggerMock,
                 null,
@@ -154,7 +154,7 @@ class RedisStoreTest extends TestCase
 
         $this->assertInstanceOf(
             RedisStore::class,
-            new RedisStore(
+            new \SimpleSAML\Module\accounting\Data\Stores\Jobs\PhpRedis\RedisStore(
                 $this->moduleConfigurationStub,
                 $this->loggerMock,
                 null,
@@ -199,7 +199,7 @@ class RedisStoreTest extends TestCase
 
         $this->redisMock->method('rPush')->willThrowException(new RedisException('test'));
 
-        $redisStore = new RedisStore(
+        $redisStore = new \SimpleSAML\Module\accounting\Data\Stores\Jobs\PhpRedis\RedisStore(
             $this->moduleConfigurationStub,
             $this->loggerMock,
             null,
@@ -245,7 +245,7 @@ class RedisStoreTest extends TestCase
             ->method('error')
             ->with($this->stringContains('Could not pop job from Redis list.'));
 
-        $redisStore = new RedisStore(
+        $redisStore = new \SimpleSAML\Module\accounting\Data\Stores\Jobs\PhpRedis\RedisStore(
             $this->moduleConfigurationStub,
             $this->loggerMock,
             null,
diff --git a/tests/src/Trackers/Authentication/DoctrineDbal/Versioned/TrackerTest.php b/tests/src/Data/Trackers/Activity/DoctrineDbal/Versioned/DataTrackerTest.php
similarity index 52%
rename from tests/src/Trackers/Authentication/DoctrineDbal/Versioned/TrackerTest.php
rename to tests/src/Data/Trackers/Activity/DoctrineDbal/Versioned/DataTrackerTest.php
index 9c51588..4a9caee 100644
--- a/tests/src/Trackers/Authentication/DoctrineDbal/Versioned/TrackerTest.php
+++ b/tests/src/Data/Trackers/Activity/DoctrineDbal/Versioned/DataTrackerTest.php
@@ -2,40 +2,41 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Trackers\Authentication\DoctrineDbal\Versioned;
+namespace SimpleSAML\Test\Module\accounting\Data\Trackers\Activity\DoctrineDbal\Versioned;
 
 use DateInterval;
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store;
+use SimpleSAML\Module\accounting\Data\Trackers\Activity\DoctrineDbal\VersionedDataTracker;
 use SimpleSAML\Module\accounting\Entities\Activity;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event;
-use SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Services\HelpersManager;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
-use SimpleSAML\Module\accounting\Trackers\Authentication\DoctrineDbal\Versioned\Tracker;
-use PHPUnit\Framework\TestCase;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Trackers\Authentication\DoctrineDbal\Versioned\Tracker
+ * @covers \SimpleSAML\Module\accounting\Data\Trackers\Activity\DoctrineDbal\VersionedDataTracker
  * @uses \SimpleSAML\Module\accounting\Helpers\InstanceBuilderUsingModuleConfiguration
- * @uses \SimpleSAML\Module\accounting\Stores\Builders\Bases\AbstractStoreBuilder
- * @uses \SimpleSAML\Module\accounting\Stores\Builders\DataStoreBuilder
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractStore
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Builders\Bases\AbstractStoreBuilder
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Builders\DataStoreBuilder
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Activity\DoctrineDbal\Versioned\Store\Repository
  * @uses \SimpleSAML\Module\accounting\Helpers\Hash
  * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Providers\Activity\DoctrineDbal\VersionedDataProvider
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository
  */
-class TrackerTest extends TestCase
+class DataTrackerTest extends TestCase
 {
     /**
      * @var Stub
@@ -48,11 +49,7 @@ class TrackerTest extends TestCase
     /**
      * @var MockObject
      */
-    protected $dataStoreMock;
-    /**
-     * @var Stub
-     */
-    protected $helpersManagerStub;
+    protected $store;
 
     protected function setUp(): void
     {
@@ -60,8 +57,7 @@ class TrackerTest extends TestCase
         $this->moduleConfigurationStub->method('getConnectionParameters')
             ->willReturn(ConnectionParameters::DBAL_SQLITE_MEMORY);
         $this->loggerMock = $this->createMock(LoggerInterface::class);
-        $this->dataStoreMock = $this->createMock(Store::class);
-        $this->helpersManagerStub = $this->createStub(HelpersManager::class);
+        $this->store = $this->createMock(Store::class);
     }
 
     /**
@@ -70,24 +66,23 @@ class TrackerTest extends TestCase
     public function testCanCreateInstance(): void
     {
         $this->assertInstanceOf(
-            Tracker::class,
-            new Tracker(
+            VersionedDataTracker::class,
+            new VersionedDataTracker(
                 $this->moduleConfigurationStub,
                 $this->loggerMock,
                 ModuleConfiguration\ConnectionType::MASTER,
-                $this->helpersManagerStub,
-                $this->dataStoreMock
+                $this->store
             )
         );
 
         $this->assertInstanceOf(
-            Tracker::class,
-            new Tracker($this->moduleConfigurationStub, $this->loggerMock)
+            VersionedDataTracker::class,
+            new VersionedDataTracker($this->moduleConfigurationStub, $this->loggerMock)
         );
 
         $this->assertInstanceOf(
-            Tracker::class,
-            Tracker::build($this->moduleConfigurationStub, $this->loggerMock)
+            VersionedDataTracker::class,
+            VersionedDataTracker::build($this->moduleConfigurationStub, $this->loggerMock)
         );
     }
 
@@ -98,16 +93,15 @@ class TrackerTest extends TestCase
     {
         $authenticationEventStub = $this->createStub(Event::class);
 
-        $this->dataStoreMock->expects($this->once())
+        $this->store->expects($this->once())
             ->method('persist')
             ->with($authenticationEventStub);
 
-        $tracker = new Tracker(
+        $tracker = new VersionedDataTracker(
             $this->moduleConfigurationStub,
             $this->loggerMock,
             ModuleConfiguration\ConnectionType::MASTER,
-            $this->helpersManagerStub,
-            $this->dataStoreMock
+            $this->store
         );
 
         $tracker->process($authenticationEventStub);
@@ -118,19 +112,18 @@ class TrackerTest extends TestCase
      */
     public function testSetupDependsOnDataStore(): void
     {
-        $this->dataStoreMock->expects($this->exactly(2))
+        $this->store->expects($this->exactly(2))
             ->method('needsSetup')
             ->willReturn(true);
 
-        $this->dataStoreMock->expects($this->once())
+        $this->store->expects($this->once())
             ->method('runSetup');
 
-        $tracker = new Tracker(
+        $tracker = new VersionedDataTracker(
             $this->moduleConfigurationStub,
             $this->loggerMock,
             ModuleConfiguration\ConnectionType::MASTER,
-            $this->helpersManagerStub,
-            $this->dataStoreMock
+            $this->store
         );
 
         $this->assertTrue($tracker->needsSetup());
@@ -143,63 +136,37 @@ class TrackerTest extends TestCase
      */
     public function testRunningSetupIfNotNeededLogsWarning(): void
     {
-        $this->dataStoreMock->method('needsSetup')
+        $this->store->method('needsSetup')
             ->willReturn(false);
 
         $this->loggerMock->expects($this->once())
             ->method('warning');
 
-        $tracker = new Tracker(
+        $tracker = new VersionedDataTracker(
             $this->moduleConfigurationStub,
             $this->loggerMock,
             ModuleConfiguration\ConnectionType::MASTER,
-            $this->helpersManagerStub,
-            $this->dataStoreMock
+            $this->store
         );
 
         $tracker->runSetup();
     }
 
-    /**
-     * @throws StoreException
-     */
-    public function testGetConnectedServiceProviders(): void
-    {
-        $connectedOrganizationsBagStub = $this->createStub(ConnectedServiceProvider\Bag::class);
-        $this->dataStoreMock->expects($this->once())
-            ->method('getConnectedOrganizations')
-            ->willReturn($connectedOrganizationsBagStub);
-
-        $tracker = new Tracker(
-            $this->moduleConfigurationStub,
-            $this->loggerMock,
-            ModuleConfiguration\ConnectionType::MASTER,
-            $this->helpersManagerStub,
-            $this->dataStoreMock
-        );
-
-        $this->assertInstanceOf(
-            ConnectedServiceProvider\Bag::class,
-            $tracker->getConnectedServiceProviders('test')
-        );
-    }
-
     /**
      * @throws StoreException
      */
     public function testGetActivity(): void
     {
         $activityBag = $this->createStub(Activity\Bag::class);
-        $this->dataStoreMock->expects($this->once())
+        $this->store->expects($this->once())
             ->method('getActivity')
             ->willReturn($activityBag);
 
-        $tracker = new Tracker(
+        $tracker = new VersionedDataTracker(
             $this->moduleConfigurationStub,
             $this->loggerMock,
             ModuleConfiguration\ConnectionType::MASTER,
-            $this->helpersManagerStub,
-            $this->dataStoreMock
+            $this->store
         );
 
         $this->assertInstanceOf(
@@ -215,15 +182,14 @@ class TrackerTest extends TestCase
     {
         $retentionPolicy = new DateInterval('P10D');
 
-        $this->dataStoreMock->expects($this->once())
+        $this->store->expects($this->once())
             ->method('deleteDataOlderThan');
 
-        $tracker = new Tracker(
+        $tracker = new VersionedDataTracker(
             $this->moduleConfigurationStub,
             $this->loggerMock,
             ModuleConfiguration\ConnectionType::MASTER,
-            $this->helpersManagerStub,
-            $this->dataStoreMock
+            $this->store
         );
 
         $tracker->enforceDataRetentionPolicy($retentionPolicy);
diff --git a/tests/src/Trackers/Builders/AuthenticationDataTrackerBuilderTest.php b/tests/src/Data/Trackers/Builders/DataTrackerBuilderTest.php
similarity index 77%
rename from tests/src/Trackers/Builders/AuthenticationDataTrackerBuilderTest.php
rename to tests/src/Data/Trackers/Builders/DataTrackerBuilderTest.php
index f22f4bb..ed5c86b 100644
--- a/tests/src/Trackers/Builders/AuthenticationDataTrackerBuilderTest.php
+++ b/tests/src/Data/Trackers/Builders/DataTrackerBuilderTest.php
@@ -2,23 +2,22 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Trackers\Builders;
+namespace SimpleSAML\Test\Module\accounting\Data\Trackers\Builders;
 
 use DateInterval;
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event;
 use SimpleSAML\Module\accounting\Exceptions\Exception;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\HelpersManager;
-use SimpleSAML\Module\accounting\Trackers\Builders\AuthenticationDataTrackerBuilder;
-use PHPUnit\Framework\TestCase;
-use SimpleSAML\Module\accounting\Trackers\Interfaces\AuthenticationDataTrackerInterface;
 use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Trackers\Builders\AuthenticationDataTrackerBuilder
+ * @covers \SimpleSAML\Module\accounting\Data\Trackers\Builders\DataTrackerBuilder
  * @uses \SimpleSAML\Module\accounting\Helpers\InstanceBuilderUsingModuleConfiguration
  * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
  */
@@ -33,7 +32,7 @@ class AuthenticationDataTrackerBuilderTest extends TestCase
      */
     protected $moduleConfigurationStub;
 
-    protected AuthenticationDataTrackerInterface $trackerStub;
+    protected DataTrackerInterface $trackerStub;
     protected HelpersManager $helpersManager;
 
     protected function setUp(): void
@@ -45,11 +44,11 @@ class AuthenticationDataTrackerBuilderTest extends TestCase
 
         $this->helpersManager = new HelpersManager();
 
-        $this->trackerStub = new class implements AuthenticationDataTrackerInterface {
+        $this->trackerStub = new class implements DataTrackerInterface {
             public static function build(
                 ModuleConfiguration $moduleConfiguration,
                 LoggerInterface $logger
-            ): AuthenticationDataTrackerInterface {
+            ): DataTrackerInterface {
                 return new self();
             }
 
@@ -75,8 +74,8 @@ class AuthenticationDataTrackerBuilderTest extends TestCase
     public function testCanCreateInstance(): void
     {
         $this->assertInstanceOf(
-            AuthenticationDataTrackerBuilder::class,
-            new AuthenticationDataTrackerBuilder(
+            \SimpleSAML\Module\accounting\Data\Trackers\Builders\DataTrackerBuilder::class,
+            new \SimpleSAML\Module\accounting\Data\Trackers\Builders\DataTrackerBuilder(
                 $this->moduleConfigurationStub,
                 $this->loggerMock,
                 $this->helpersManager
@@ -89,7 +88,7 @@ class AuthenticationDataTrackerBuilderTest extends TestCase
      */
     public function testCanBuildAuthenticationDataTracker(): void
     {
-        $authenticationDataTrackerBuilder = new AuthenticationDataTrackerBuilder(
+        $authenticationDataTrackerBuilder = new \SimpleSAML\Module\accounting\Data\Trackers\Builders\DataTrackerBuilder(
             $this->moduleConfigurationStub,
             $this->loggerMock,
             $this->helpersManager
@@ -102,7 +101,7 @@ class AuthenticationDataTrackerBuilderTest extends TestCase
 
     public function testBuildThrowsForInvalidTrackerClass(): void
     {
-        $authenticationDataTrackerBuilder = new AuthenticationDataTrackerBuilder(
+        $authenticationDataTrackerBuilder = new \SimpleSAML\Module\accounting\Data\Trackers\Builders\DataTrackerBuilder(
             $this->moduleConfigurationStub,
             $this->loggerMock,
             $this->helpersManager
diff --git a/tests/src/Data/Trackers/ConnectedServices/DoctrineDbal/Versioned/DataTrackerTest.php b/tests/src/Data/Trackers/ConnectedServices/DoctrineDbal/Versioned/DataTrackerTest.php
new file mode 100644
index 0000000..29fef34
--- /dev/null
+++ b/tests/src/Data/Trackers/ConnectedServices/DoctrineDbal/Versioned/DataTrackerTest.php
@@ -0,0 +1,199 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Test\Module\accounting\Data\Trackers\ConnectedServices\DoctrineDbal\Versioned;
+
+use DateInterval;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store;
+use SimpleSAML\Module\accounting\Data\Trackers\ConnectedServices\DoctrineDbal\VersionedDataTracker;
+use SimpleSAML\Module\accounting\Entities\Authentication\Event;
+use SimpleSAML\Module\accounting\Entities\ConnectedService;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
+
+/**
+ * @covers \SimpleSAML\Module\accounting\Data\Trackers\ConnectedServices\DoctrineDbal\VersionedDataTracker
+ * @uses \SimpleSAML\Module\accounting\Helpers\InstanceBuilderUsingModuleConfiguration
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Builders\Bases\AbstractStoreBuilder
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Builders\DataStoreBuilder
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Factory
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\DoctrineDbal\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\ConnectedServices\DoctrineDbal\Versioned\Store\Repository
+ * @uses \SimpleSAML\Module\accounting\Helpers\Hash
+ * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Bases\AbstractStore
+ * @uses \SimpleSAML\Module\accounting\Data\Providers\ConnectedServices\DoctrineDbal\VersionedDataProvider
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store
+ * @uses \SimpleSAML\Module\accounting\Data\Stores\Accounting\Bases\DoctrineDbal\Versioned\Store\Repository
+ */
+class DataTrackerTest extends TestCase
+{
+    /**
+     * @var Stub
+     */
+    protected $moduleConfigurationStub;
+    /**
+     * @var MockObject
+     */
+    protected $loggerMock;
+    /**
+     * @var MockObject
+     */
+    protected $store;
+
+    protected function setUp(): void
+    {
+        $this->moduleConfigurationStub = $this->createStub(ModuleConfiguration::class);
+        $this->moduleConfigurationStub->method('getConnectionParameters')
+            ->willReturn(ConnectionParameters::DBAL_SQLITE_MEMORY);
+        $this->loggerMock = $this->createMock(LoggerInterface::class);
+        $this->store = $this->createMock(
+            Store::class
+        );
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testCanCreateInstance(): void
+    {
+        $this->assertInstanceOf(
+            VersionedDataTracker::class,
+            new VersionedDataTracker(
+                $this->moduleConfigurationStub,
+                $this->loggerMock,
+                ModuleConfiguration\ConnectionType::MASTER,
+                $this->store
+            )
+        );
+
+        $this->assertInstanceOf(
+            VersionedDataTracker::class,
+            new VersionedDataTracker($this->moduleConfigurationStub, $this->loggerMock)
+        );
+
+        $this->assertInstanceOf(
+            VersionedDataTracker::class,
+            VersionedDataTracker::build($this->moduleConfigurationStub, $this->loggerMock)
+        );
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testProcessCallsPersistOnDataStore(): void
+    {
+        $authenticationEventStub = $this->createStub(Event::class);
+
+        $this->store->expects($this->once())
+            ->method('persist')
+            ->with($authenticationEventStub);
+
+        $tracker = new VersionedDataTracker(
+            $this->moduleConfigurationStub,
+            $this->loggerMock,
+            ModuleConfiguration\ConnectionType::MASTER,
+            $this->store
+        );
+
+        $tracker->process($authenticationEventStub);
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testSetupDependsOnDataStore(): void
+    {
+        $this->store->expects($this->exactly(2))
+            ->method('needsSetup')
+            ->willReturn(true);
+
+        $this->store->expects($this->once())
+            ->method('runSetup');
+
+        $tracker = new VersionedDataTracker(
+            $this->moduleConfigurationStub,
+            $this->loggerMock,
+            ModuleConfiguration\ConnectionType::MASTER,
+            $this->store
+        );
+
+        $this->assertTrue($tracker->needsSetup());
+
+        $tracker->runSetup();
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testRunningSetupIfNotNeededLogsWarning(): void
+    {
+        $this->store->method('needsSetup')
+            ->willReturn(false);
+
+        $this->loggerMock->expects($this->once())
+            ->method('warning');
+
+        $tracker = new VersionedDataTracker(
+            $this->moduleConfigurationStub,
+            $this->loggerMock,
+            ModuleConfiguration\ConnectionType::MASTER,
+            $this->store
+        );
+
+        $tracker->runSetup();
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testGetConnectedServices(): void
+    {
+        $connectedOrganizationsBagStub = $this->createStub(ConnectedService\Bag::class);
+        $this->store->expects($this->once())
+            ->method('getConnectedServices')
+            ->willReturn($connectedOrganizationsBagStub);
+
+        $tracker = new VersionedDataTracker(
+            $this->moduleConfigurationStub,
+            $this->loggerMock,
+            ModuleConfiguration\ConnectionType::MASTER,
+            $this->store
+        );
+
+        $this->assertInstanceOf(
+            ConnectedService\Bag::class,
+            $tracker->getConnectedServices('test')
+        );
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function testCanEnforceDataRetentionPolicy(): void
+    {
+        $retentionPolicy = new DateInterval('P10D');
+
+        $this->store->expects($this->once())
+            ->method('deleteDataOlderThan');
+
+        $tracker = new VersionedDataTracker(
+            $this->moduleConfigurationStub,
+            $this->loggerMock,
+            ModuleConfiguration\ConnectionType::MASTER,
+            $this->store
+        );
+
+        $tracker->enforceDataRetentionPolicy($retentionPolicy);
+    }
+}
diff --git a/tests/src/Entities/ConnectedServiceProvider/BagTest.php b/tests/src/Entities/ConnectedService/BagTest.php
similarity index 73%
rename from tests/src/Entities/ConnectedServiceProvider/BagTest.php
rename to tests/src/Entities/ConnectedService/BagTest.php
index 396c455..87c3b75 100644
--- a/tests/src/Entities/ConnectedServiceProvider/BagTest.php
+++ b/tests/src/Entities/ConnectedService/BagTest.php
@@ -2,20 +2,20 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Test\Module\accounting\Entities\ConnectedServiceProvider;
+namespace SimpleSAML\Test\Module\accounting\Entities\ConnectedService;
 
-use SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider;
-use SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider\Bag;
+use SimpleSAML\Module\accounting\Entities\ConnectedService;
+use SimpleSAML\Module\accounting\Entities\ConnectedService\Bag;
 use PHPUnit\Framework\TestCase;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider\Bag
+ * @covers \SimpleSAML\Module\accounting\Entities\ConnectedService\Bag
  */
 class BagTest extends TestCase
 {
     public function testCanAddConnectedService(): void
     {
-        $connectedServiceProvider = $this->createStub(ConnectedServiceProvider::class);
+        $connectedServiceProvider = $this->createStub(ConnectedService::class);
         $bag = new Bag();
 
         $this->assertEmpty($bag->getAll());
diff --git a/tests/src/Entities/ConnectedServiceProviderTest.php b/tests/src/Entities/ConnectedServiceTest.php
similarity index 89%
rename from tests/src/Entities/ConnectedServiceProviderTest.php
rename to tests/src/Entities/ConnectedServiceTest.php
index 6dcee02..86c03e5 100644
--- a/tests/src/Entities/ConnectedServiceProviderTest.php
+++ b/tests/src/Entities/ConnectedServiceTest.php
@@ -6,15 +6,15 @@ namespace SimpleSAML\Test\Module\accounting\Entities;
 
 use DateTimeImmutable;
 use PHPUnit\Framework\MockObject\Stub;
-use SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider;
+use SimpleSAML\Module\accounting\Entities\ConnectedService;
 use PHPUnit\Framework\TestCase;
 use SimpleSAML\Module\accounting\Entities\Interfaces\ServiceProviderInterface;
 use SimpleSAML\Module\accounting\Entities\User;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Entities\ConnectedServiceProvider
+ * @covers \SimpleSAML\Module\accounting\Entities\ConnectedService
  */
-class ConnectedServiceProviderTest extends TestCase
+class ConnectedServiceTest extends TestCase
 {
     /**
      * @var Stub
@@ -37,7 +37,7 @@ class ConnectedServiceProviderTest extends TestCase
 
     public function testCanCreateInstance(): void
     {
-        $connectedServiceProvider = new ConnectedServiceProvider(
+        $connectedServiceProvider = new ConnectedService(
             $this->serviceProviderStub,
             $this->numberOfAuthentications,
             $this->dateTime,
diff --git a/tests/src/ModuleConfigurationTest.php b/tests/src/ModuleConfigurationTest.php
index 7af5525..77a9114 100644
--- a/tests/src/ModuleConfigurationTest.php
+++ b/tests/src/ModuleConfigurationTest.php
@@ -6,10 +6,13 @@ namespace SimpleSAML\Test\Module\accounting;
 
 use PHPUnit\Framework\TestCase;
 use SimpleSAML\Configuration;
+use SimpleSAML\Module\accounting\Data\Providers\Activity\DoctrineDbal\VersionedDataProvider;
+use SimpleSAML\Module\accounting\Data\Stores;
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\JobsStoreInterface;
+use SimpleSAML\Module\accounting\Data\Stores\Jobs\DoctrineDbal\Store;
+use SimpleSAML\Module\accounting\Data\Trackers;
 use SimpleSAML\Module\accounting\Exceptions\InvalidConfigurationException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Stores;
-use SimpleSAML\Module\accounting\Trackers;
 use stdClass;
 
 /**
@@ -56,7 +59,7 @@ class ModuleConfigurationTest extends TestCase
     public function testCanGetJobsStoreClass(): void
     {
         $this->assertTrue(
-            is_subclass_of($this->moduleConfiguration->getJobsStoreClass(), Stores\Interfaces\JobsStoreInterface::class)
+            is_subclass_of($this->moduleConfiguration->getJobsStoreClass(), JobsStoreInterface::class)
         );
     }
 
@@ -96,7 +99,9 @@ class ModuleConfigurationTest extends TestCase
     {
         $this->assertSame(
             'doctrine_dbal_pdo_sqlite',
-            $this->moduleConfiguration->getClassConnectionKey(Stores\Jobs\DoctrineDbal\Store::class)
+            $this->moduleConfiguration->getClassConnectionKey(
+                Store::class
+            )
         );
     }
 
@@ -105,7 +110,7 @@ class ModuleConfigurationTest extends TestCase
         $this->assertSame(
             'doctrine_dbal_pdo_sqlite_slave',
             $this->moduleConfiguration->getClassConnectionKey(
-                Trackers\Authentication\DoctrineDbal\Versioned\Tracker::class,
+                VersionedDataProvider::class,
                 ModuleConfiguration\ConnectionType::SLAVE
             )
         );
@@ -152,7 +157,7 @@ class ModuleConfigurationTest extends TestCase
         $this->expectException(InvalidConfigurationException::class);
 
         $this->moduleConfiguration->getClassConnectionKey(
-            Stores\Jobs\DoctrineDbal\Store::class,
+            Store::class,
             'invalid'
         );
     }
@@ -311,14 +316,14 @@ class ModuleConfigurationTest extends TestCase
     /**
      * @throws \Exception
      */
-    public function testThrowsOnInvalidDefaultDataTrackerAndProvider(): void
+    public function testThrowsOnInvalidDataProvider(): void
     {
         $this->expectException(InvalidConfigurationException::class);
 
         new ModuleConfiguration(
             null,
             [
-                ModuleConfiguration::OPTION_DEFAULT_DATA_TRACKER_AND_PROVIDER => 'invalid'
+                ModuleConfiguration::OPTION_PROVIDER_FOR_ACTIVITY => 'invalid'
             ]
         );
     }
diff --git a/tests/src/Providers/Builders/AuthenticationDataProviderBuilderTest.php b/tests/src/Providers/Builders/AuthenticationDataProviderBuilderTest.php
deleted file mode 100644
index 089baaa..0000000
--- a/tests/src/Providers/Builders/AuthenticationDataProviderBuilderTest.php
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace SimpleSAML\Test\Module\accounting\Providers\Builders;
-
-use PHPUnit\Framework\MockObject\Stub;
-use Psr\Log\LoggerInterface;
-use SimpleSAML\Module\accounting\Exceptions\Exception;
-use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Providers\Builders\AuthenticationDataProviderBuilder;
-use PHPUnit\Framework\TestCase;
-use SimpleSAML\Module\accounting\Services\HelpersManager;
-use SimpleSAML\Module\accounting\Trackers\Authentication\DoctrineDbal\Versioned\Tracker;
-use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
-
-/**
- * @covers \SimpleSAML\Module\accounting\Providers\Builders\AuthenticationDataProviderBuilder
- * @uses \SimpleSAML\Module\accounting\Helpers\InstanceBuilderUsingModuleConfiguration
- * @uses \SimpleSAML\Module\accounting\Stores\Builders\Bases\AbstractStoreBuilder
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\DoctrineDbal\AbstractStore
- * @uses \SimpleSAML\Module\accounting\Stores\Builders\DataStoreBuilder
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Repository
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store
- * @uses \SimpleSAML\Module\accounting\Trackers\Authentication\DoctrineDbal\Versioned\Tracker
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator
- * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
- * @uses \SimpleSAML\Module\accounting\Stores\Bases\AbstractStore
- */
-class AuthenticationDataProviderBuilderTest extends TestCase
-{
-    protected Stub $moduleConfigurationStub;
-
-    protected Stub $loggerStub;
-    protected HelpersManager $helpersManager;
-
-    protected function setUp(): void
-    {
-        $this->moduleConfigurationStub = $this->createStub(ModuleConfiguration::class);
-        $connectionParams = ConnectionParameters::DBAL_SQLITE_MEMORY;
-        $this->moduleConfigurationStub->method('getConnectionParameters')
-            ->willReturn($connectionParams);
-
-        $this->loggerStub = $this->createStub(LoggerInterface::class);
-        $this->helpersManager = new HelpersManager();
-    }
-
-    public function testCanCreateInstance(): void
-    {
-        $this->assertInstanceOf(
-            AuthenticationDataProviderBuilder::class,
-            new AuthenticationDataProviderBuilder(
-                $this->moduleConfigurationStub,
-                $this->loggerStub,
-                $this->helpersManager
-            )
-        );
-    }
-
-    /**
-     * @throws Exception
-     */
-    public function testCanBuildDataProvider(): void
-    {
-        $builder = new AuthenticationDataProviderBuilder(
-            $this->moduleConfigurationStub,
-            $this->loggerStub,
-            $this->helpersManager
-        );
-
-        $this->assertInstanceOf(Tracker::class, $builder->build(Tracker::class));
-    }
-
-    public function testThrowsForInvalidClass(): void
-    {
-        $this->expectException(Exception::class);
-
-        (new AuthenticationDataProviderBuilder(
-            $this->moduleConfigurationStub,
-            $this->loggerStub,
-            $this->helpersManager
-        ))->build('invalid');
-    }
-}
diff --git a/tests/src/Services/JobRunnerTest.php b/tests/src/Services/JobRunnerTest.php
index 5c748b2..35b4002 100644
--- a/tests/src/Services/JobRunnerTest.php
+++ b/tests/src/Services/JobRunnerTest.php
@@ -8,11 +8,14 @@ use DateInterval;
 use DateTimeImmutable;
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\MockObject\Stub;
+use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 use Psr\SimpleCache\CacheInterface;
 use SimpleSAML\Configuration;
+use SimpleSAML\Module\accounting\Data\Stores\Builders\JobsStoreBuilder;
+use SimpleSAML\Module\accounting\Data\Stores\Interfaces\JobsStoreInterface;
+use SimpleSAML\Module\accounting\Data\Trackers\Interfaces\DataTrackerInterface;
 use SimpleSAML\Module\accounting\Entities\Authentication\Event;
-use SimpleSAML\Module\accounting\Entities\Bases\AbstractPayload;
 use SimpleSAML\Module\accounting\Entities\Interfaces\JobInterface;
 use SimpleSAML\Module\accounting\Exceptions\Exception;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
@@ -22,11 +25,7 @@ use SimpleSAML\Module\accounting\Helpers\Random;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\HelpersManager;
 use SimpleSAML\Module\accounting\Services\JobRunner;
-use PHPUnit\Framework\TestCase;
-use SimpleSAML\Module\accounting\Stores\Builders\JobsStoreBuilder;
-use SimpleSAML\Module\accounting\Stores\Interfaces\JobsStoreInterface;
-use SimpleSAML\Module\accounting\Trackers\Builders\AuthenticationDataTrackerBuilder;
-use SimpleSAML\Module\accounting\Trackers\Interfaces\AuthenticationDataTrackerInterface;
+use SimpleSAML\Module\accounting\Services\TrackerResolver;
 
 /**
  * @covers \SimpleSAML\Module\accounting\Services\JobRunner
@@ -58,13 +57,13 @@ class JobRunnerTest extends TestCase
      */
     protected $rateLimiterMock;
     /**
-     * @var Stub
+     * @var MockObject
      */
-    protected $authenticationDataTrackerBuilderStub;
+    protected $trackerResolverMock;
     /**
      * @var MockObject
      */
-    protected $authenticationDataTrackerMock;
+    protected $dataTrackerMock;
     /**
      * @var Stub
      */
@@ -103,8 +102,8 @@ class JobRunnerTest extends TestCase
         $this->moduleConfigurationStub = $this->createStub(ModuleConfiguration::class);
         $this->sspConfigurationStub = $this->createStub(Configuration::class);
         $this->loggerMock = $this->createMock(LoggerInterface::class);
-        $this->authenticationDataTrackerBuilderStub = $this->createStub(AuthenticationDataTrackerBuilder::class);
-        $this->authenticationDataTrackerMock = $this->createMock(AuthenticationDataTrackerInterface::class);
+        $this->trackerResolverMock = $this->createMock(TrackerResolver::class);
+        $this->dataTrackerMock = $this->createMock(DataTrackerInterface::class);
         $this->jobsStoreBuilderStub = $this->createStub(JobsStoreBuilder::class);
         $this->cacheMock = $this->createMock(CacheInterface::class);
         $this->stateStub = $this->createStub(JobRunner\State::class);
@@ -133,7 +132,7 @@ class JobRunnerTest extends TestCase
                 $this->sspConfigurationStub,
                 $this->loggerMock,
                 $this->helpersManagerStub,
-                $this->authenticationDataTrackerBuilderStub,
+                $this->trackerResolverMock,
                 $this->jobsStoreBuilderStub,
                 $this->cacheMock,
                 $this->stateStub,
@@ -169,7 +168,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -206,7 +205,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -239,7 +238,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -277,7 +276,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -318,7 +317,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -353,7 +352,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -395,7 +394,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -437,7 +436,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -486,7 +485,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -525,7 +524,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -566,7 +565,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -607,7 +606,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -643,14 +642,14 @@ class JobRunnerTest extends TestCase
         $this->cacheMock->expects($this->once())->method('delete');
 
         $this->loggerMock->expects($this->once())->method('warning')
-            ->with($this->stringContains('Current job runner ID differs from the ID in the cached state.'));
+            ->with($this->stringContains('CurrentDataProvider job runner ID differs from the ID in the cached state.'));
 
         $jobRunner = new JobRunner(
             $this->moduleConfigurationStub,
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -694,7 +693,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -739,7 +738,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -794,7 +793,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -846,7 +845,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -864,8 +863,8 @@ class JobRunnerTest extends TestCase
     {
         $this->moduleConfigurationStub->method('getAccountingProcessingType')
             ->willReturn(ModuleConfiguration\AccountingProcessingType::VALUE_ASYNCHRONOUS);
-        $this->moduleConfigurationStub->method('getDefaultDataTrackerAndProviderClass')
-            ->willReturn('mock');
+        $this->moduleConfigurationStub->method('getProviderClasses')
+            ->willReturn(['mocks']);
 
         $this->randomHelperStub->method('getInt')->willReturn(123);
         $this->helpersManagerStub->method('getRandom')->willReturn($this->randomHelperStub);
@@ -889,10 +888,8 @@ class JobRunnerTest extends TestCase
 
         $this->rateLimiterMock->expects($this->once())->method('resetBackoffPause');
 
-        $this->authenticationDataTrackerMock->expects($this->once())
-            ->method('process');
-        $this->authenticationDataTrackerBuilderStub->method('build')
-            ->willReturn($this->authenticationDataTrackerMock);
+        $this->dataTrackerMock->expects($this->once())->method('process');
+        $this->trackerResolverMock->method('fromModuleConfiguration')->willReturn([$this->dataTrackerMock]);
 
         $this->jobStub->method('getPayload')->willReturn($this->payloadStub);
         $this->jobsStoreMock->method('dequeue')->willReturn($this->jobStub);
@@ -914,7 +911,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -931,8 +928,8 @@ class JobRunnerTest extends TestCase
     {
         $this->moduleConfigurationStub->method('getAccountingProcessingType')
             ->willReturn(ModuleConfiguration\AccountingProcessingType::VALUE_ASYNCHRONOUS);
-        $this->moduleConfigurationStub->method('getDefaultDataTrackerAndProviderClass')
-            ->willReturn('mock');
+        $this->moduleConfigurationStub->method('getProviderClasses')
+            ->willReturn(['mocks']);
 
         $this->randomHelperStub->method('getInt')->willReturn(123);
         $this->helpersManagerStub->method('getRandom')->willReturn($this->randomHelperStub);
@@ -975,7 +972,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -993,8 +990,8 @@ class JobRunnerTest extends TestCase
     {
         $this->moduleConfigurationStub->method('getAccountingProcessingType')
             ->willReturn(ModuleConfiguration\AccountingProcessingType::VALUE_ASYNCHRONOUS);
-        $this->moduleConfigurationStub->method('getDefaultDataTrackerAndProviderClass')
-            ->willReturn('mock');
+        $this->moduleConfigurationStub->method('getProviderClasses')
+            ->willReturn(['mocks']);
         $this->moduleConfigurationStub->method('getJobRunnerShouldPauseAfterNumberOfJobsProcessed')
             ->willReturn(0);
 
@@ -1022,10 +1019,9 @@ class JobRunnerTest extends TestCase
         $this->rateLimiterMock->expects($this->exactly(2))->method('resetBackoffPause');
         $this->rateLimiterMock->expects($this->once())->method('doPause');
 
-        $this->authenticationDataTrackerMock->expects($this->exactly(2))
+        $this->dataTrackerMock->expects($this->exactly(2))
             ->method('process');
-        $this->authenticationDataTrackerBuilderStub->method('build')
-            ->willReturn($this->authenticationDataTrackerMock);
+        $this->trackerResolverMock->method('fromModuleConfiguration')->willReturn([$this->dataTrackerMock]);
 
         $this->jobStub->method('getPayload')->willReturn($this->payloadStub);
         $this->jobsStoreMock->method('dequeue')->willReturn($this->jobStub);
@@ -1048,7 +1044,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -1066,8 +1062,8 @@ class JobRunnerTest extends TestCase
     {
         $this->moduleConfigurationStub->method('getAccountingProcessingType')
             ->willReturn(ModuleConfiguration\AccountingProcessingType::VALUE_ASYNCHRONOUS);
-        $this->moduleConfigurationStub->method('getDefaultDataTrackerAndProviderClass')
-            ->willReturn('mock');
+        $this->moduleConfigurationStub->method('getProviderClasses')
+            ->willReturn(['mocks']);
 
         $this->randomHelperStub->method('getInt')->willReturn(123);
         $this->helpersManagerStub->method('getRandom')->willReturn($this->randomHelperStub);
@@ -1091,11 +1087,10 @@ class JobRunnerTest extends TestCase
 
         $this->rateLimiterMock->expects($this->once())->method('resetBackoffPause');
 
-        $this->authenticationDataTrackerMock->expects($this->once())
+        $this->dataTrackerMock->expects($this->once())
             ->method('process')
             ->willThrowException(new Exception('test'));
-        $this->authenticationDataTrackerBuilderStub->method('build')
-            ->willReturn($this->authenticationDataTrackerMock);
+        $this->trackerResolverMock->method('fromModuleConfiguration')->willReturn([$this->dataTrackerMock]);
 
         $this->jobStub->method('getPayload')->willReturn($this->payloadStub);
         $this->jobsStoreMock->method('dequeue')->willReturn($this->jobStub);
@@ -1115,7 +1110,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
@@ -1132,8 +1127,8 @@ class JobRunnerTest extends TestCase
     {
         $this->moduleConfigurationStub->method('getAccountingProcessingType')
             ->willReturn(ModuleConfiguration\AccountingProcessingType::VALUE_ASYNCHRONOUS);
-        $this->moduleConfigurationStub->method('getDefaultDataTrackerAndProviderClass')
-            ->willReturn('mock');
+        $this->moduleConfigurationStub->method('getProviderClasses')
+            ->willReturn(['mocks']);
 
         $this->randomHelperStub->method('getInt')->willReturn(123);
         $this->helpersManagerStub->method('getRandom')->willReturn($this->randomHelperStub);
@@ -1162,7 +1157,7 @@ class JobRunnerTest extends TestCase
             $this->sspConfigurationStub,
             $this->loggerMock,
             $this->helpersManagerStub,
-            $this->authenticationDataTrackerBuilderStub,
+            $this->trackerResolverMock,
             $this->jobsStoreBuilderStub,
             $this->cacheMock,
             $this->stateStub,
diff --git a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTableTest.php b/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTableTest.php
deleted file mode 100644
index 1c18391..0000000
--- a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/Migrations/Version20220801000600CreateIdpSpUserVersionTableTest.php
+++ /dev/null
@@ -1,107 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace SimpleSAML\Test\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
-
-use Doctrine\DBAL\Exception;
-use Doctrine\DBAL\Schema\AbstractSchemaManager;
-use PHPUnit\Framework\MockObject\Stub;
-use SimpleSAML\Module\accounting\Exceptions\StoreException;
-use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use PHPUnit\Framework\TestCase;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations;
-use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
-
-/**
- * @covers \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000600CreateIdpSpUserVersionTable
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection *
- */
-class Version20220801000600CreateIdpSpUserVersionTableTest extends TestCase
-{
-    protected Connection $connection;
-    protected AbstractSchemaManager $schemaManager;
-    protected string $tableName;
-    protected Stub $connectionStub;
-    protected Stub $dbalStub;
-    protected Stub $schemaManagerStub;
-
-    /**
-     * @throws Exception
-     */
-    protected function setUp(): void
-    {
-        $this->connection = new Connection(ConnectionParameters::DBAL_SQLITE_MEMORY);
-        $this->schemaManager = $this->connection->dbal()->createSchemaManager();
-        $this->tableName = 'vds_idp_sp_user_version';
-
-        $this->connectionStub = $this->createStub(Connection::class);
-        $this->dbalStub = $this->createStub(\Doctrine\DBAL\Connection::class);
-        $this->schemaManagerStub = $this->createStub(AbstractSchemaManager::class);
-    }
-
-    /**
-     * @throws StoreException
-     * @throws MigrationException
-     * @throws Exception
-     */
-    public function testCanRunMigration(): void
-    {
-        $this->assertFalse($this->schemaManager->tablesExist($this->tableName));
-        $migration =
-            new Migrations\Version20220801000600CreateIdpSpUserVersionTable($this->connection);
-        $migration->run();
-        $this->assertTrue($this->schemaManager->tablesExist($this->tableName));
-        $migration->revert();
-        $this->assertFalse($this->schemaManager->tablesExist($this->tableName));
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public function testRunThrowsMigrationException(): void
-    {
-        $this->connectionStub->method('preparePrefixedTableName')->willReturn($this->tableName);
-        $this->schemaManagerStub->method('createTable')
-            ->willThrowException(new Exception('test'));
-        $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub);
-        $this->connectionStub->method('dbal')->willReturn($this->dbalStub);
-
-        $migration =
-            new Migrations\Version20220801000600CreateIdpSpUserVersionTable($this->connectionStub);
-        $this->expectException(MigrationException::class);
-        $migration->run();
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public function testRevertThrowsMigrationException(): void
-    {
-        $this->schemaManagerStub->method('dropTable')
-            ->willThrowException(new Exception('test'));
-        $this->dbalStub->method('createSchemaManager')->willReturn($this->schemaManagerStub);
-        $this->connectionStub->method('dbal')->willReturn($this->dbalStub);
-
-        $migration =
-            new Migrations\Version20220801000600CreateIdpSpUserVersionTable($this->connectionStub);
-        $this->expectException(MigrationException::class);
-        $migration->revert();
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public function testRunThrowsOnIvalidTableNameIdp(): void
-    {
-        $this->connectionStub->method('preparePrefixedTableName')
-            ->willReturnOnConsecutiveCalls(''); // Invalid (empty) name for table
-
-        $migration =
-            new Migrations\Version20220801000600CreateIdpSpUserVersionTable($this->connectionStub);
-        $this->expectException(MigrationException::class);
-        $migration->run();
-    }
-}
diff --git a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RepositoryTest.php b/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RepositoryTest.php
deleted file mode 100644
index bf93c30..0000000
--- a/tests/src/Stores/Data/Authentication/DoctrineDbal/Versioned/Store/RepositoryTest.php
+++ /dev/null
@@ -1,849 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace SimpleSAML\Test\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
-
-use DateInterval;
-use DateTimeImmutable;
-use Exception;
-use PHPUnit\Framework\MockObject\Stub;
-use Psr\Log\LoggerInterface;
-use SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2;
-use SimpleSAML\Module\accounting\Exceptions\StoreException;
-use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
-use SimpleSAML\Module\accounting\ModuleConfiguration;
-use SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
-use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store;
-use SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Repository;
-use PHPUnit\Framework\TestCase;
-use SimpleSAML\Test\Module\accounting\Constants\ConnectionParameters;
-use SimpleSAML\Test\Module\accounting\Constants\DateTime;
-
-/**
- * @covers \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Repository
- * @uses \SimpleSAML\Module\accounting\Helpers\Filesystem
- * @uses \SimpleSAML\Module\accounting\ModuleConfiguration
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
- * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000000CreateIdpTable
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000100CreateIdpVersionTable
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000200CreateSpTable
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000300CreateSpVersionTable
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000400CreateUserTable
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000500CreateUserVersionTable
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000600CreateIdpSpUserVersionTable
- * @uses \SimpleSAML\Module\accounting\Stores\Data\Authentication\DoctrineDbal\Versioned\Store\Migrations\Version20220801000700CreateAuthenticationEventTable
- * @uses \SimpleSAML\Module\accounting\Services\HelpersManager
- */
-class RepositoryTest extends TestCase
-{
-    protected Connection $connection;
-    protected \Doctrine\DBAL\Connection $dbal;
-    /**
-     * @var Stub
-     */
-    protected $loggerStub;
-    protected Migrator $migrator;
-    protected string $dateTimeFormat;
-    protected string $idpEntityId;
-    protected string $idpEntityIdHash;
-    protected string $idpMetadata;
-    protected string $idpMetadataHash;
-    protected string $spEntityId;
-    protected string $spMetadataHash;
-    protected string $userIdentifier;
-    protected string $userIdentifierHash;
-    protected string $userAttributes;
-    protected string $userAttributesHash;
-    protected Repository $repository;
-    protected DateTimeImmutable $createdAt;
-    /**
-     * @var Stub
-     */
-    protected $connectionStub;
-    protected string $spEntityIdHash;
-    protected string $spMetadata;
-    protected string $clientIpAddress;
-    protected string $authenticationProtocolDesignation;
-
-    /**
-     * @throws StoreException
-     * @throws MigrationException
-     */
-    protected function setUp(): void
-    {
-        // For stubbing.
-        $this->connectionStub = $this->createStub(Connection::class);
-        $this->loggerStub = $this->createStub(LoggerInterface::class);
-
-        // For real DB testing.
-        $connectionParameters = ConnectionParameters::DBAL_SQLITE_MEMORY;
-        $this->connection = new Connection($connectionParameters);
-        $this->migrator = new Migrator($this->connection, $this->loggerStub);
-        $moduleConfiguration = new ModuleConfiguration();
-        $migrationsDirectory = $moduleConfiguration->getModuleSourceDirectory() . DIRECTORY_SEPARATOR . 'Stores' .
-            DIRECTORY_SEPARATOR . 'Data' . DIRECTORY_SEPARATOR . 'Authentication' . DIRECTORY_SEPARATOR .
-            'DoctrineDbal' . DIRECTORY_SEPARATOR . 'Versioned' . DIRECTORY_SEPARATOR . 'Store' . DIRECTORY_SEPARATOR .
-            AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME;
-        $namespace = Store::class . '\\' . AbstractMigrator::DEFAULT_MIGRATIONS_DIRECTORY_NAME;
-
-        $this->migrator->runSetup();
-        $this->migrator->runNonImplementedMigrationClasses($migrationsDirectory, $namespace);
-
-        $this->repository = new Repository($this->connection, $this->loggerStub);
-
-        $this->dateTimeFormat = DateTime::DEFAULT_FORMAT;
-
-        $this->idpEntityId = 'idp-entity-id';
-        $this->idpEntityIdHash = 'idp-entity-id-hash';
-
-        $this->idpMetadata = 'idp-metadata';
-        $this->idpMetadataHash = 'idp-metadata-hash';
-
-        $this->spEntityId = 'sp-entity-id';
-        $this->spEntityIdHash = 'sp-entity-id-hash';
-
-        $this->spMetadata = 'sp-metadata';
-        $this->spMetadataHash = 'sp-metadata-hash';
-
-        $this->userIdentifier = 'user-identifier';
-        $this->userIdentifierHash = 'user-identifier-hash';
-
-        $this->userAttributes = 'user-attributes';
-        $this->userAttributesHash = 'user-attributes-hash';
-
-        $this->createdAt = new DateTimeImmutable();
-        $this->clientIpAddress = '123.123.123.123';
-        $this->authenticationProtocolDesignation = Saml2::DESIGNATION;
-    }
-
-    public function testCanCreateInstance(): void
-    {
-        $this->assertInstanceOf(
-            Repository::class,
-            new Repository($this->connection, $this->loggerStub)
-        );
-    }
-
-    /**
-     * @throws StoreException
-     * @throws \Doctrine\DBAL\Exception
-     */
-    public function testCanInsertAndGetIdp(): array
-    {
-        $this->repository->insertIdp($this->idpEntityId, $this->idpEntityIdHash, $this->createdAt);
-
-        $result = $this->repository->getIdp($this->idpEntityIdHash)->fetchAssociative();
-
-        $this->assertSame($this->idpEntityId, $result[Store\TableConstants::TABLE_IDP_COLUMN_NAME_ENTITY_ID]);
-        $this->assertSame(
-            $this->idpEntityIdHash,
-            $result[Store\TableConstants::TABLE_IDP_COLUMN_NAME_ENTITY_ID_HASH_SHA256]
-        );
-        $this->assertSame(
-            $this->createdAt->format($this->dateTimeFormat),
-            $result[Store\TableConstants::TABLE_IDP_COLUMN_NAME_CREATED_AT]
-        );
-
-        return $result;
-    }
-
-    public function testInsertIdpThrowsOnNonUniqueIdpEntityIdHash(): void
-    {
-        $this->expectException(StoreException::class);
-
-        // Can't insert duplicate idp entity ID hash.
-        $this->repository->insertIdp($this->idpEntityId, $this->idpEntityIdHash, $this->createdAt);
-        $this->repository->insertIdp($this->idpEntityId, $this->idpEntityIdHash, $this->createdAt);
-    }
-
-    public function testGetIdpThrowsOnInvalidDbal(): void
-    {
-        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
-        $repository = new Repository($this->connectionStub, $this->loggerStub);
-        $this->expectException(StoreException::class);
-
-        $repository->getIdp($this->idpEntityIdHash);
-    }
-
-    /**
-     * @depends testCanInsertAndGetIdp
-     * @throws StoreException
-     * @throws StoreException
-     * @throws \Doctrine\DBAL\Exception
-     */
-    public function testCanInsertAndGetIdpVersion(array $idpResult): array
-    {
-        $idpId = (int)$idpResult[Store\TableConstants::TABLE_IDP_COLUMN_NAME_ID];
-
-        $this->repository->insertIdpVersion($idpId, $this->idpMetadata, $this->idpMetadataHash, $this->createdAt);
-
-        $result = $this->repository->getIdpVersion($idpId, $this->idpMetadataHash)->fetchAssociative();
-
-        $this->assertSame($this->idpMetadata, $result[Store\TableConstants::TABLE_IDP_VERSION_COLUMN_NAME_METADATA]);
-        $this->assertSame(
-            $this->idpMetadataHash,
-            $result[Store\TableConstants::TABLE_IDP_VERSION_COLUMN_NAME_METADATA_HASH_SHA256]
-        );
-        $this->assertSame(
-            $this->createdAt->format($this->dateTimeFormat),
-            $result[Store\TableConstants::TABLE_IDP_VERSION_COLUMN_NAME_CREATED_AT]
-        );
-
-        return $result;
-    }
-
-    public function testInsertIdpVersionThrowsOnNonUniqueIdpMetadataHash(): void
-    {
-        $this->expectException(StoreException::class);
-        // IdP Metadata Hash must be unique.
-        $this->repository->insertIdpVersion(1, $this->idpMetadata, $this->idpMetadataHash, $this->createdAt);
-        $this->repository->insertIdpVersion(1, $this->idpMetadata, $this->idpMetadataHash, $this->createdAt);
-    }
-
-    public function testGetIdpVersionThrowsOnInvalidDbal(): void
-    {
-        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
-        $repository = new Repository($this->connectionStub, $this->loggerStub);
-        $this->expectException(StoreException::class);
-
-        $repository->getIdpVersion(1, $this->idpMetadataHash);
-    }
-
-    /**
-     * @throws StoreException
-     * @throws \Doctrine\DBAL\Exception
-     */
-    public function testCanInsertAndGetSp(): array
-    {
-        $this->repository->insertSp($this->spEntityId, $this->spEntityIdHash, $this->createdAt);
-
-        $result = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative();
-
-        $this->assertSame($this->spEntityId, $result[Store\TableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID]);
-        $this->assertSame(
-            $this->spEntityIdHash,
-            $result[Store\TableConstants::TABLE_SP_COLUMN_NAME_ENTITY_ID_HASH_SHA256]
-        );
-        $this->assertSame(
-            $this->createdAt->format($this->dateTimeFormat),
-            $result[Store\TableConstants::TABLE_SP_COLUMN_NAME_CREATED_AT]
-        );
-
-        return $result;
-    }
-
-    public function testInsertSpThrowsOnNonUniqueSpEntityIdHash(): void
-    {
-        $this->expectException(StoreException::class);
-        // SP Entity ID Hash must be unique.
-        $this->repository->insertSp($this->spEntityId, $this->spEntityIdHash, $this->createdAt);
-        $this->repository->insertSp($this->spEntityId, $this->spEntityIdHash, $this->createdAt);
-    }
-
-    public function testGetSpThrowsOnInvalidDbal(): void
-    {
-        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
-        $repository = new Repository($this->connectionStub, $this->loggerStub);
-        $this->expectException(StoreException::class);
-
-        $repository->getSp($this->spEntityIdHash);
-    }
-
-    /**
-     * @depends testCanInsertAndGetSp
-     * @throws StoreException
-     * @throws StoreException
-     * @throws \Doctrine\DBAL\Exception
-     */
-    public function testCanInsertAndGetSpVersion(array $spResult): array
-    {
-        $spId = (int)$spResult[Store\TableConstants::TABLE_SP_COLUMN_NAME_ID];
-
-        $this->repository->insertSpVersion($spId, $this->spMetadata, $this->spMetadataHash, $this->createdAt);
-
-        $result = $this->repository->getSpVersion($spId, $this->spMetadataHash)->fetchAssociative();
-
-        $this->assertSame($this->spMetadata, $result[Store\TableConstants::TABLE_SP_VERSION_COLUMN_NAME_METADATA]);
-        $this->assertSame(
-            $this->spMetadataHash,
-            $result[Store\TableConstants::TABLE_SP_VERSION_COLUMN_NAME_METADATA_HASH_SHA256]
-        );
-        $this->assertSame(
-            $this->createdAt->format($this->dateTimeFormat),
-            $result[Store\TableConstants::TABLE_SP_VERSION_COLUMN_NAME_CREATED_AT]
-        );
-
-        return $result;
-    }
-
-    public function testInsertSpVersionThrowsOnNonUniqueMetadataHash(): void
-    {
-        $this->expectException(StoreException::class);
-        // SP metadata hash must be unique.
-        $this->repository->insertSpVersion(1, $this->spMetadata, $this->spMetadataHash, $this->createdAt);
-        $this->repository->insertSpVersion(1, $this->spMetadata, $this->spMetadataHash, $this->createdAt);
-    }
-
-    public function testGetSpVersionThrowsOnInvalidDbal(): void
-    {
-        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
-        $repository = new Repository($this->connectionStub, $this->loggerStub);
-        $this->expectException(StoreException::class);
-
-        $repository->getSpVersion(1, $this->spMetadataHash);
-    }
-
-    /**
-     * @throws StoreException
-     * @throws \Doctrine\DBAL\Exception
-     */
-    public function testCanInsertAndGetUser(): array
-    {
-        $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt);
-
-        $result = $this->repository->getUser($this->userIdentifierHash)->fetchAssociative();
-
-        $this->assertSame($this->userIdentifier, $result[Store\TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER]);
-        $this->assertSame(
-            $this->userIdentifierHash,
-            $result[Store\TableConstants::TABLE_USER_COLUMN_NAME_IDENTIFIER_HASH_SHA256]
-        );
-        $this->assertSame(
-            $this->createdAt->format($this->dateTimeFormat),
-            $result[Store\TableConstants::TABLE_USER_COLUMN_NAME_CREATED_AT]
-        );
-
-        return $result;
-    }
-
-    public function testInsertUserThrowsOnNonUniqueIdentifierHash(): void
-    {
-        $this->expectException(StoreException::class);
-        $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt);
-        $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt);
-    }
-
-    public function testGetUserThrowsOnInvalidDbal(): void
-    {
-        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
-        $repository = new Repository($this->connectionStub, $this->loggerStub);
-        $this->expectException(StoreException::class);
-
-        $repository->getUser($this->userIdentifierHash);
-    }
-
-    /**
-     * @depends testCanInsertAndGetUser
-     * @throws StoreException
-     * @throws StoreException
-     * @throws \Doctrine\DBAL\Exception
-     */
-    public function testCanInsertAndGetUserVersion(array $userResult): array
-    {
-        $userId = (int)$userResult[Store\TableConstants::TABLE_USER_COLUMN_NAME_ID];
-
-        $this->repository
-            ->insertUserVersion($userId, $this->userAttributes, $this->userAttributesHash, $this->createdAt);
-
-        $result = $this->repository->getUserVersion($userId, $this->userAttributesHash)->fetchAssociative();
-
-        $this->assertSame(
-            $this->userAttributes,
-            $result[Store\TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES]
-        );
-        $this->assertSame(
-            $this->userAttributesHash,
-            $result[Store\TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ATTRIBUTES_HASH_SHA256]
-        );
-        $this->assertSame(
-            $this->createdAt->format($this->dateTimeFormat),
-            $result[Store\TableConstants::TABLE_USER_VERSION_COLUMN_NAME_CREATED_AT]
-        );
-
-        return $result;
-    }
-
-    public function testInsertUserVersionThrowsOnNonUniqueAttributesHash(): void
-    {
-        $this->expectException(StoreException::class);
-        $this->repository
-            ->insertUserVersion(1, $this->userAttributes, $this->userAttributesHash, $this->createdAt);
-        $this->repository
-            ->insertUserVersion(1, $this->userAttributes, $this->userAttributesHash, $this->createdAt);
-    }
-
-    public function testGetUserVersionThrowsOnInvalidDbal(): void
-    {
-        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
-        $repository = new Repository($this->connectionStub, $this->loggerStub);
-        $this->expectException(StoreException::class);
-
-        $repository->getUserVersion(1, $this->userIdentifierHash);
-    }
-
-    /**
-     * @depends testCanInsertAndGetIdpVersion
-     * @depends testCanInsertAndGetSpVersion
-     * @depends testCanInsertAndGetUserVersion
-     * @throws StoreException
-     * @throws StoreException
-     * @throws \Doctrine\DBAL\Exception
-     */
-    public function testCanInsertAndGetIdpSpUserVersion(
-        array $idpVersionResult,
-        array $spVersionResult,
-        array $userVersionResult
-    ): array {
-        $idpVersionId = (int)$idpVersionResult[Store\TableConstants::TABLE_IDP_VERSION_COLUMN_NAME_ID];
-        $spVersionId = (int)$spVersionResult[Store\TableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID];
-        $userVersionId = (int)$userVersionResult[Store\TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID];
-
-        $this->repository->insertIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId, $this->createdAt);
-        $result = $this->repository->getIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId)
-            ->fetchAssociative();
-
-        $this->assertSame(
-            $idpVersionId,
-            (int)$result[Store\TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_IDP_VERSION_ID]
-        );
-        $this->assertSame(
-            $spVersionId,
-            (int)$result[Store\TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID]
-        );
-        $this->assertSame(
-            $userVersionId,
-            (int)$result[Store\TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_SP_VERSION_ID]
-        );
-        $this->assertSame(
-            $this->createdAt->format($this->dateTimeFormat),
-            $result[Store\TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_CREATED_AT]
-        );
-
-        return $result;
-    }
-
-    public function testInsertIdpSpUserVersionThrowsOnNonUnique(): void
-    {
-        $this->expectException(StoreException::class);
-        $this->repository->insertIdpSpUserVersion(1, 1, 1, $this->createdAt);
-        $this->repository->insertIdpSpUserVersion(1, 1, 1, $this->createdAt);
-    }
-
-    public function testGetIdpSpUserVersionThrowsOnInvalidDbal(): void
-    {
-        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
-        $repository = new Repository($this->connectionStub, $this->loggerStub);
-        $this->expectException(StoreException::class);
-
-        $repository->getIdpSpUserVersion(1, 1, 1);
-    }
-
-    /**
-     * @depends testCanInsertAndGetIdpSpUserVersion
-     * @throws \Doctrine\DBAL\Exception
-     * @throws \Doctrine\DBAL\Exception
-     * @throws StoreException
-     * @throws \Doctrine\DBAL\Exception
-     * @throws \Doctrine\DBAL\Exception
-     */
-    public function testCanInsertAuthenticationEvent(array $idpSpUserVersionResult): void
-    {
-        $idpSpUserVersionId =
-            (int)$idpSpUserVersionResult[Store\TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
-        $createdAt = $happenedAt = new DateTimeImmutable();
-
-        $authenticationEventCounterQueryBuilder = $this->connection->dbal()->createQueryBuilder();
-        $authenticationEventCounterQueryBuilder->select('COUNT(id) as authenticationEventCount')
-            ->from(
-                Store\TableConstants::TABLE_PREFIX .
-                $this->connection
-                    ->preparePrefixedTableName(Store\TableConstants::TABLE_NAME_AUTHENTICATION_EVENT)
-            );
-
-        $this->assertSame(0, (int)$authenticationEventCounterQueryBuilder->executeQuery()->fetchOne());
-
-        $this->repository->insertAuthenticationEvent($idpSpUserVersionId, $happenedAt, null, null, $createdAt);
-
-        $this->assertSame(1, (int)$authenticationEventCounterQueryBuilder->executeQuery()->fetchOne());
-    }
-
-    public function testInsertAuthenticationEventThrowsOnInvalidDbal(): void
-    {
-        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
-        $repository = new Repository($this->connectionStub, $this->loggerStub);
-        $this->expectException(StoreException::class);
-
-        $repository->insertAuthenticationEvent(1, $this->createdAt);
-    }
-
-    /**
-     * @throws StoreException
-     * @throws \Doctrine\DBAL\Exception
-     */
-    public function testCanGetConnectedServiceProviders(): void
-    {
-        $this->repository->insertIdp($this->idpEntityId, $this->idpEntityIdHash, $this->createdAt);
-        $idpResult = $this->repository->getIdp($this->idpEntityIdHash)->fetchAssociative();
-        $idpId = (int)$idpResult[Store\TableConstants::TABLE_IDP_COLUMN_NAME_ID];
-        $this->repository->insertIdpVersion($idpId, $this->idpMetadata, $this->idpMetadataHash, $this->createdAt);
-        $idpVersionResult = $this->repository->getIdpVersion($idpId, $this->idpMetadataHash)->fetchAssociative();
-
-        $this->repository->insertSp($this->spEntityId, $this->spEntityIdHash, $this->createdAt);
-        $spResult = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative();
-        $spId = (int)$spResult[Store\TableConstants::TABLE_SP_COLUMN_NAME_ID];
-        $this->repository->insertSpVersion($spId, $this->spMetadata, $this->spMetadataHash, $this->createdAt);
-        $spVersionResult = $this->repository->getSpVersion($spId, $this->spMetadataHash)->fetchAssociative();
-
-        $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt);
-        $userResult = $this->repository->getUser($this->userIdentifierHash)->fetchAssociative();
-        $userId = (int)$userResult[Store\TableConstants::TABLE_USER_COLUMN_NAME_ID];
-        $this->repository
-            ->insertUserVersion($userId, $this->userAttributes, $this->userAttributesHash, $this->createdAt);
-        $userVersionResult = $this->repository->getUserVersion($userId, $this->userAttributesHash)->fetchAssociative();
-
-        $idpVersionId = (int)$idpVersionResult[Store\TableConstants::TABLE_IDP_VERSION_COLUMN_NAME_ID];
-        $spVersionId = (int)$spVersionResult[Store\TableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID];
-        $userVersionId = (int)$userVersionResult[Store\TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID];
-
-        $this->repository->insertIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId, $this->createdAt);
-        $idpSpUserVersionResult = $this->repository->getIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId)
-            ->fetchAssociative();
-
-        $idpSpUserVersionId =
-            (int)$idpSpUserVersionResult[Store\TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
-
-        $this->repository->insertAuthenticationEvent(
-            $idpSpUserVersionId,
-            $this->createdAt,
-            $this->clientIpAddress,
-            $this->authenticationProtocolDesignation,
-            $this->createdAt
-        );
-
-        $resultArray = $this->repository->getConnectedServiceProviders($this->userIdentifierHash);
-
-        $this->assertCount(1, $resultArray);
-        $this->assertEquals(
-            '1',
-            $resultArray[$this->spEntityId]
-            [Store\TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS]
-        );
-        $this->assertSame(
-            $this->spMetadata,
-            $resultArray[$this->spEntityId]
-            [Store\TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_SP_METADATA]
-        );
-        $this->assertSame(
-            $this->userAttributes,
-            $resultArray[$this->spEntityId]
-            [Store\TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_USER_ATTRIBUTES]
-        );
-
-        $resultArray = $this->repository->getConnectedServiceProviders($this->userIdentifierHash);
-        $this->assertCount(1, $resultArray);
-
-        $this->repository->insertAuthenticationEvent(
-            $idpSpUserVersionId,
-            $this->createdAt,
-            $this->clientIpAddress,
-            $this->authenticationProtocolDesignation,
-            $this->createdAt
-        );
-        $resultArray = $this->repository->getConnectedServiceProviders($this->userIdentifierHash);
-        $this->assertCount(1, $resultArray);
-        $this->assertEquals(
-            '2',
-            $resultArray[$this->spEntityId]
-            [Store\TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS]
-        );
-        $this->assertSame(
-            $this->spMetadata,
-            $resultArray[$this->spEntityId]
-            [Store\TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_SP_METADATA]
-        );
-        $this->assertSame(
-            $this->userAttributes,
-            $resultArray[$this->spEntityId]
-            [Store\TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_USER_ATTRIBUTES]
-        );
-
-        // Simulate another SP
-        $spEntityIdNew = $this->spEntityId . '-new';
-        $spEntityIdHashNew = $this->spEntityIdHash . '-new';
-        $spMetadataNew = $this->spMetadata . '-new';
-        $spMetadataHashNew = $this->spMetadataHash . '-new';
-        $this->repository->insertSp($spEntityIdNew, $spEntityIdHashNew, $this->createdAt);
-        $spResult = $this->repository->getSp($spEntityIdHashNew)->fetchAssociative();
-        $spId = (int)$spResult[Store\TableConstants::TABLE_SP_COLUMN_NAME_ID];
-        $this->repository->insertSpVersion($spId, $spMetadataNew, $spMetadataHashNew, $this->createdAt);
-        $spVersionResult = $this->repository->getSpVersion($spId, $spMetadataHashNew)->fetchAssociative();
-        $spVersionId = (int)$spVersionResult[Store\TableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID];
-
-        $this->repository->insertIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId, $this->createdAt);
-        $idpSpUserVersionResult = $this->repository->getIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId)
-            ->fetchAssociative();
-        $idpSpUserVersionId =
-            (int)$idpSpUserVersionResult[Store\TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
-
-        $this->repository->insertAuthenticationEvent(
-            $idpSpUserVersionId,
-            $this->createdAt,
-            $this->clientIpAddress,
-            $this->authenticationProtocolDesignation,
-            $this->createdAt
-        );
-
-        $resultArray = $this->repository->getConnectedServiceProviders($this->userIdentifierHash);
-        $this->assertCount(2, $resultArray);
-        $this->assertEquals(
-            '1',
-            $resultArray[$spEntityIdNew]
-            [Store\TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS]
-        );
-        $this->assertSame(
-            $spMetadataNew,
-            $resultArray[$spEntityIdNew]
-            [Store\TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_SP_METADATA]
-        );
-        $this->assertSame(
-            $this->userAttributes,
-            $resultArray[$this->spEntityId]
-            [Store\TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_USER_ATTRIBUTES]
-        );
-
-        // Simulate change in user attributes
-        $userAttributesNew = $this->userAttributes . '-new';
-        $userAttributesHashNew = $this->userAttributesHash . '-new';
-        $this->repository->insertUserVersion($userId, $userAttributesNew, $userAttributesHashNew, $this->createdAt);
-        $userVersionResult = $this->repository->getUserVersion($userId, $userAttributesHashNew)->fetchAssociative();
-        $userVersionId = (int)$userVersionResult[Store\TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID];
-
-        $this->repository->insertIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId, $this->createdAt);
-
-        $idpSpUserVersionResult = $this->repository->getIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId)
-            ->fetchAssociative();
-        $idpSpUserVersionId =
-            (int)$idpSpUserVersionResult[Store\TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
-
-        $this->repository->insertAuthenticationEvent(
-            $idpSpUserVersionId,
-            $this->createdAt,
-            $this->clientIpAddress,
-            $this->authenticationProtocolDesignation,
-            $this->createdAt
-        );
-        $resultArray = $this->repository->getConnectedServiceProviders($this->userIdentifierHash);
-
-        $this->assertCount(2, $resultArray);
-        $this->assertEquals(
-            '2',
-            $resultArray[$spEntityIdNew]
-            [Store\TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_NUMBER_OF_AUTHENTICATIONS]
-        );
-        $this->assertSame(
-            $spMetadataNew,
-            $resultArray[$spEntityIdNew]
-            [Store\TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_SP_METADATA]
-        );
-        // New SP with new user attributes version..
-        $this->assertSame(
-            $userAttributesNew,
-            $resultArray[$spEntityIdNew]
-            [Store\TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_USER_ATTRIBUTES]
-        );
-
-        // First SP still has old user attributes version...
-        $this->assertSame(
-            $this->userAttributes,
-            $resultArray[$this->spEntityId]
-            [Store\TableConstants::ENTITY_CONNECTED_ORGANIZATION_COLUMN_NAME_USER_ATTRIBUTES]
-        );
-    }
-
-    public function testGetConnectedServiceProvidersThrowsOnInvalidDbal(): void
-    {
-        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
-        $repository = new Repository($this->connectionStub, $this->loggerStub);
-        $this->expectException(StoreException::class);
-
-        $repository->getConnectedServiceProviders($this->userIdentifierHash);
-    }
-
-    /**
-     * @throws StoreException
-     * @throws \Doctrine\DBAL\Exception
-     */
-    public function testCanGetActivity(): void
-    {
-        $this->repository->insertIdp($this->idpEntityId, $this->idpEntityIdHash, $this->createdAt);
-        $idpResult = $this->repository->getIdp($this->idpEntityIdHash)->fetchAssociative();
-        $idpId = (int)$idpResult[Store\TableConstants::TABLE_IDP_COLUMN_NAME_ID];
-        $this->repository->insertIdpVersion($idpId, $this->idpMetadata, $this->idpMetadataHash, $this->createdAt);
-        $idpVersionResult = $this->repository->getIdpVersion($idpId, $this->idpMetadataHash)->fetchAssociative();
-
-        $this->repository->insertSp($this->spEntityId, $this->spEntityIdHash, $this->createdAt);
-        $spResult = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative();
-        $spId = (int)$spResult[Store\TableConstants::TABLE_SP_COLUMN_NAME_ID];
-        $this->repository->insertSpVersion($spId, $this->spMetadata, $this->spMetadataHash, $this->createdAt);
-        $spVersionResult = $this->repository->getSpVersion($spId, $this->spMetadataHash)->fetchAssociative();
-
-        $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt);
-        $userResult = $this->repository->getUser($this->userIdentifierHash)->fetchAssociative();
-        $userId = (int)$userResult[Store\TableConstants::TABLE_USER_COLUMN_NAME_ID];
-        $this->repository
-            ->insertUserVersion($userId, $this->userAttributes, $this->userAttributesHash, $this->createdAt);
-        $userVersionResult = $this->repository->getUserVersion($userId, $this->userAttributesHash)->fetchAssociative();
-
-        $idpVersionId = (int)$idpVersionResult[Store\TableConstants::TABLE_IDP_VERSION_COLUMN_NAME_ID];
-        $spVersionId = (int)$spVersionResult[Store\TableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID];
-        $userVersionId = (int)$userVersionResult[Store\TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID];
-
-        $this->repository->insertIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId, $this->createdAt);
-        $idpSpUserVersionResult = $this->repository->getIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId)
-            ->fetchAssociative();
-
-        $idpSpUserVersionId =
-            (int)$idpSpUserVersionResult[Store\TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
-
-        $this->repository->insertAuthenticationEvent(
-            $idpSpUserVersionId,
-            $this->createdAt,
-            $this->clientIpAddress,
-            $this->authenticationProtocolDesignation,
-            $this->createdAt
-        );
-
-        $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0);
-        $this->assertCount(1, $resultArray);
-
-        $this->repository->insertAuthenticationEvent(
-            $idpSpUserVersionId,
-            $this->createdAt,
-            $this->clientIpAddress,
-            $this->authenticationProtocolDesignation,
-            $this->createdAt
-        );
-        $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0);
-        $this->assertCount(2, $resultArray);
-
-        $this->repository->insertAuthenticationEvent(
-            $idpSpUserVersionId,
-            $this->createdAt,
-            $this->clientIpAddress,
-            $this->authenticationProtocolDesignation,
-            $this->createdAt
-        );
-        $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0);
-        $this->assertCount(3, $resultArray);
-
-        // Simulate another SP
-        $spEntityIdNew = $this->spEntityId . '-new';
-        $spEntityIdHashNew = $this->spEntityIdHash . '-new';
-        $spMetadataNew = $this->spMetadata . '-new';
-        $spMetadataHashNew = $this->spMetadataHash . '-new';
-        $this->repository->insertSp($spEntityIdNew, $spEntityIdHashNew, $this->createdAt);
-        $spResult = $this->repository->getSp($spEntityIdHashNew)->fetchAssociative();
-        $spId = (int)$spResult[Store\TableConstants::TABLE_SP_COLUMN_NAME_ID];
-        $this->repository->insertSpVersion($spId, $spMetadataNew, $spMetadataHashNew, $this->createdAt);
-        $spVersionResult = $this->repository->getSpVersion($spId, $spMetadataHashNew)->fetchAssociative();
-        $spVersionId = (int)$spVersionResult[Store\TableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID];
-
-        $this->repository->insertIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId, $this->createdAt);
-        $idpSpUserVersionResult = $this->repository->getIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId)
-            ->fetchAssociative();
-
-        $idpSpUserVersionId =
-            (int)$idpSpUserVersionResult[Store\TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
-
-        $this->repository->insertAuthenticationEvent(
-            $idpSpUserVersionId,
-            $this->createdAt,
-            $this->clientIpAddress,
-            $this->authenticationProtocolDesignation,
-            $this->createdAt
-        );
-        $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0);
-        $this->assertCount(4, $resultArray);
-
-        // Simulate a change in user attributes
-    }
-
-    public function testGetActivityThrowsOnInvalidDbal(): void
-    {
-        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
-        $repository = new Repository($this->connectionStub, $this->loggerStub);
-        $this->expectException(StoreException::class);
-
-        $repository->getActivity($this->userIdentifierHash, 10, 0);
-    }
-
-    /**
-     * @throws StoreException
-     * @throws \Doctrine\DBAL\Exception
-     */
-    public function testCanDeleteAuthenticationEventsOlderThan(): void
-    {
-        $this->repository->insertIdp($this->idpEntityId, $this->idpEntityIdHash, $this->createdAt);
-        $idpResult = $this->repository->getIdp($this->idpEntityIdHash)->fetchAssociative();
-        $idpId = (int)$idpResult[Store\TableConstants::TABLE_IDP_COLUMN_NAME_ID];
-        $this->repository->insertIdpVersion($idpId, $this->idpMetadata, $this->idpMetadataHash, $this->createdAt);
-        $idpVersionResult = $this->repository->getIdpVersion($idpId, $this->idpMetadataHash)->fetchAssociative();
-
-        $this->repository->insertSp($this->spEntityId, $this->spEntityIdHash, $this->createdAt);
-        $spResult = $this->repository->getSp($this->spEntityIdHash)->fetchAssociative();
-        $spId = (int)$spResult[Store\TableConstants::TABLE_SP_COLUMN_NAME_ID];
-        $this->repository->insertSpVersion($spId, $this->spMetadata, $this->spMetadataHash, $this->createdAt);
-        $spVersionResult = $this->repository->getSpVersion($spId, $this->spMetadataHash)->fetchAssociative();
-
-        $this->repository->insertUser($this->userIdentifier, $this->userIdentifierHash, $this->createdAt);
-        $userResult = $this->repository->getUser($this->userIdentifierHash)->fetchAssociative();
-        $userId = (int)$userResult[Store\TableConstants::TABLE_USER_COLUMN_NAME_ID];
-        $this->repository
-            ->insertUserVersion($userId, $this->userAttributes, $this->userAttributesHash, $this->createdAt);
-        $userVersionResult = $this->repository->getUserVersion($userId, $this->userAttributesHash)->fetchAssociative();
-
-        $idpVersionId = (int)$idpVersionResult[Store\TableConstants::TABLE_IDP_VERSION_COLUMN_NAME_ID];
-        $spVersionId = (int)$spVersionResult[Store\TableConstants::TABLE_SP_VERSION_COLUMN_NAME_ID];
-        $userVersionId = (int)$userVersionResult[Store\TableConstants::TABLE_USER_VERSION_COLUMN_NAME_ID];
-
-        $this->repository->insertIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId, $this->createdAt);
-        $idpSpUserVersionResult = $this->repository->getIdpSpUserVersion($idpVersionId, $spVersionId, $userVersionId)
-            ->fetchAssociative();
-
-        $idpSpUserVersionId =
-            (int)$idpSpUserVersionResult[Store\TableConstants::TABLE_IDP_SP_USER_VERSION_COLUMN_NAME_ID];
-
-        $this->repository->insertAuthenticationEvent(
-            $idpSpUserVersionId,
-            $this->createdAt,
-            $this->clientIpAddress,
-            $this->authenticationProtocolDesignation,
-            $this->createdAt
-        );
-
-        $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0);
-        $this->assertCount(1, $resultArray);
-
-        $dateTimeInFuture = $this->createdAt->add(new DateInterval('P1D'));
-
-        $this->repository->deleteAuthenticationEventsOlderThan($dateTimeInFuture);
-
-        $resultArray = $this->repository->getActivity($this->userIdentifierHash, 10, 0);
-        $this->assertCount(0, $resultArray);
-    }
-
-    public function testDeleteAuthenticationEventsOlderThanThrowsOnInvalidDbal(): void
-    {
-        $this->connectionStub->method('dbal')->willThrowException(new Exception('test'));
-        $repository = new Repository($this->connectionStub, $this->loggerStub);
-        $this->expectException(StoreException::class);
-
-        $repository->deleteAuthenticationEventsOlderThan(new DateTimeImmutable());
-    }
-}
-- 
GitLab