diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index e800ac65..fc040549 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -16,12 +16,12 @@ jobs:
 
     strategy:
       matrix:
-        php: [ '7.3', '7.4', '8.0' ]
+        php: [ '8.2' ]
 
     steps:
       - name: Checkout
         uses: actions/checkout@v2
-        
+
       - name: Start mysql service
         run: |
           echo -e "\n[mysqld]\nserver-id=1\nbinlog_format=row\nlog_bin=/var/log/mysql/mysql-bin.log" | sudo tee -a /etc/mysql/my.cnf
diff --git a/.gitignore b/.gitignore
index bcc3a2ed..8a895654 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,5 @@ composer.phar
 composer.lock
 .php_cs.cache
 /example/profiler.php
-.idea/
\ No newline at end of file
+.idea/
+.cache/
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 720f22a0..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,88 +0,0 @@
-dist: xenial
-
-language: php
-
-cache:
-  bundler: true
-  directories:
-    - $HOME/.composer/cache
-
-sudo: required
-
-services:
-  - docker
-
-matrix:
-  include:
-    - env:
-        - DB=mariadb:5.5
-      php: "7.3"
-    - env:
-        - DB=mariadb:10.0
-      php: "7.3"
-    - env:
-        - DB=mariadb:10.1
-      php: "7.3"
-    - env:
-        - DB=mysql:5.5
-      php: "7.3"
-    - env:
-        - DB=mysql:5.6
-      php: "7.3"
-    - env:
-        - DB=mysql:5.7
-      php: "7.3"
-    - env:
-        - DB=mysql:8.0
-        - TEST_AUTH=yes
-      php: "7.3"
-    - env:
-        - DB=mariadb:5.5
-      php: "7.4"
-    - env:
-        - DB=mariadb:10.0
-      php: "7.4"
-    - env:
-        - DB=mariadb:10.1
-      php: "7.4"
-    - env:
-        - DB=mysql:5.5
-      php: "7.4"
-    - env:
-        - DB=mysql:5.6
-      php: "7.4"
-    - env:
-        - DB=mysql:5.7
-      php: "7.4"
-    - env:
-        - DB=mysql:8.0
-        - TEST_AUTH=yes
-      php: "7.4"
-    - env:
-        - DB=mariadb:5.5
-      php: "8.0"
-    - env:
-        - DB=mariadb:10.0
-      php: "8.0"
-    - env:
-        - DB=mariadb:10.1
-      php: "8.0"
-    - env:
-        - DB=mysql:5.5
-      php: "8.0"
-    - env:
-        - DB=mysql:5.6
-      php: "8.0"
-    - env:
-        - DB=mysql:5.7
-      php: "8.0"
-    - env:
-        - DB=mysql:8.0
-        - TEST_AUTH=yes
-      php: "8.0"
-
-before_script:
-  - ./.travis/initializedb.sh
-
-install:
-  travis_retry composer install --no-interaction --prefer-source;
diff --git a/.travis/initializedb.sh b/.travis/initializedb.sh
deleted file mode 100755
index c7fab8de..00000000
--- a/.travis/initializedb.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-
-set -ex
-
-if [ $DB == 'mysql:8.0' ]; then
-    docker run -p 0.0.0.0:3306:3306 -e MYSQL_ROOT_PASSWORD=root -e MYSQL_ROOT_HOST=% --name=mysql -d ${DB} \
-    mysqld \
-      --datadir=/var/lib/mysql \
-      --user=mysql \
-      --server-id=1 \
-      --log-bin=/var/lib/mysql/mysql-bin.log \
-      --binlog-format=row \
-      --max_allowed_packet=64M \
-      --default_authentication_plugin=mysql_native_password
-else
-    docker run -p 0.0.0.0:3306:3306 -e MYSQL_ROOT_PASSWORD=root -e MYSQL_ROOT_HOST=% --name=mysql -d ${DB} \
-    mysqld \
-      --datadir=/var/lib/mysql \
-      --user=mysql \
-      --server-id=1 \
-      --log-bin=/var/lib/mysql/mysql-bin.log \
-      --binlog-format=row \
-      --max_allowed_packet=64M
-fi
-
-mysql() {
-  docker exec mysql mysql -proot "${@}"
-}
-while :
-do
-  sleep 3
-  mysql --protocol=tcp -e 'select version()' && break
-done
-
-docker logs mysql
-
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b326daa9..e5cc6b1c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,22 @@
 # Release Notes
 
+## v8.0.0 (2024-01-29)
+
+- Change: drop support for < 8.2
+- Change: moved to enums, promoted properties
+- Added: logger for more socket info
+- Added: slave_uuid support (#99)
+- Change: EventInfo->id is now EventInfo->serverId (#83)
+- Change: config no longer static (#94)
+- Chore: typos in README/code
+- Chore: replace/remove old dead doc urls from code
+- Chore: changed variables to underscore 
+- Added: support caching_sha2_password (#102)
+- Change: BinLogServerInfo static calls removed also added method getServerInfo to MySQLReplicationFactory 
+- Change: type of bin log position is now string as it can be bigger then php can hande 2^64-1 (#84) 
+
 ## v7.0.1 (2021-03-09)
+
  - Fixed negative number handling (#80)
 
 ## v7.0.0 (2021-01-24)
diff --git a/README.md b/README.md
index 110a4d0e..20d212e3 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,10 @@
 php-mysql-replication
 =========
-[![Build Status](https://travis-ci.org/krowinski/php-mysql-replication.svg?branch=master)](https://travis-ci.org/krowinski/php-mysql-replication)
-[![Latest Stable Version](https://poser.pugx.org/krowinski/php-mysql-replication/v/stable)](https://packagist.org/packages/krowinski/php-mysql-replication) [![Total Downloads](https://poser.pugx.org/krowinski/php-mysql-replication/downloads)](https://packagist.org/packages/krowinski/php-mysql-replication) [![Latest Unstable Version](https://poser.pugx.org/krowinski/php-mysql-replication/v/unstable)](https://packagist.org/packages/krowinski/php-mysql-replication) 
-[![SensioLabsInsight](https://insight.sensiolabs.com/projects/4a0e49d4-3802-41d3-bb32-0a8194d0fd4d/mini.png)](https://insight.sensiolabs.com/projects/4a0e49d4-3802-41d3-bb32-0a8194d0fd4d) [![License](https://poser.pugx.org/krowinski/php-mysql-replication/license)](https://packagist.org/packages/krowinski/php-mysql-replication)
+[![Latest Stable Version](https://poser.pugx.org/krowinski/php-mysql-replication/v/stable)](https://packagist.org/packages/krowinski/php-mysql-replication) [![Total Downloads](https://poser.pugx.org/krowinski/php-mysql-replication/downloads)](https://packagist.org/packages/krowinski/php-mysql-replication) [![Latest Unstable Version](https://poser.pugx.org/krowinski/php-mysql-replication/v/unstable)](https://packagist.org/packages/krowinski/php-mysql-replication)
 [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/krowinski/php-mysql-replication/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/krowinski/php-mysql-replication/?branch=master)
 [![Code Coverage](https://scrutinizer-ci.com/g/krowinski/php-mysql-replication/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/krowinski/php-mysql-replication/?branch=master)
 
-Pure PHP Implementation of MySQL replication protocol. This allow you to receive event like insert, update, delete with their data and raw SQL queries.
+Pure PHP Implementation of MySQL replication protocol. This allows you to receive event like insert, update, delete with their data and raw SQL queries.
 
 Based on a great work of creators:https://github.com/noplay/python-mysql-replication and https://github.com/fengxiangyun/mysql-replication
 
@@ -29,11 +27,16 @@ composer install -o
 
 Compatibility (based on integration tests) 
 =========
+PHP
 
+- php 8.2
+- php 8.3
+
+MYSQL
  - mysql 5.5
  - mysql 5.6
  - mysql 5.7
- - mysql 8.0 (ONLY with mysql_native_password)
+ - mysql 8.0 (mysql_native_password and caching_sha2_password supported)
  - mariadb 5.5
  - mariadb 10.0
  - mariadb 10.1
@@ -83,7 +86,7 @@ Available options:
 
 'mariaDbGtid' - MariaDB GTID marker(s) to start from (format 1-1-3,0-1-88)
 
-'slaveId' - script slave id for identification (SHOW SLAVE HOSTS)
+'slaveId' - script slave id for identification (default: 666) (SHOW SLAVE HOSTS)
 
 'binLogFileName' - bin log file name to start from
 
@@ -103,6 +106,8 @@ Available options:
 
 'heartbeatPeriod' - sets the interval in seconds between replication heartbeats. Whenever the master's binary log is updated with an event, the waiting period for the next heartbeat is reset. interval is a decimal value having the range 0 to 4294967 seconds and a resolution in milliseconds; the smallest nonzero value is 0.001. Heartbeats are sent by the master only if there are no unsent events in the binary log file for a period longer than interval.
 
+'saveUuid' - sets slave uuid for identification (default: 0015d2b6-8a06-4e5e-8c07-206ef3fbd274)
+
 Similar projects
 =========
 Ruby: https://github.com/y310/kodama
@@ -394,11 +399,19 @@ FAQ
 =========
 
 1. ### Why and when need php-mysql-replication ?
- Well first of all mysql don't give you async calls. You usually need to program this in your application (by event dispaching and adding to some queue system and if your db have many point of entry like web, backend other microservices its not always cheap to add processing to all of them. But using mysql replication protocol you can lisen on write events and process then asynchronously (best combo it's to add item to some queue system like rabbitmq, redis or kafka). Also in invalidate cache,  search engine replication, real time analytics and audits.
 
-2. ### It's awsome ! but what is the catch ?
- Well first of all you need to know that a lot of events may come through, like if you update 1 000 000  records in table   "bar" and you need this one insert from your table "foo" Then all must be processed by script and you need to wait for your data. This is normal and this how it's work. You can speed up using [config options](https://github.com/krowinski/php-mysql-replication#configuration).
-Also if script crashes you need to save from time to time position form binlog (or gtid) to start from this position when  you run this script again to avoid duplicates.
+Well first of all MYSQL don't give you async calls. You usually need to program this in your application (by event dispatching and adding to some queue system
+and if your db have many point of entry like web, backend other microservices its not always cheap to add processing to all of them. But using mysql replication
+protocol you can listen on write events and process then asynchronously (the best combo it's to add item to some queue system like rabbitmq, redis or kafka).
+Also in invalidate cache, search engine replication, real time analytics and audits.
+
+2. ### It's awesome ! but what is the catch ?
+
+Well first of all you need to know that a lot of events may come through, like if you update 1 000 000 records in table "bar" and you need this one insert from
+your table "foo" Then all must be processed by script, and you need to wait for your data. This is normal and this how it's work. You can speed up
+using [config options](https://github.com/krowinski/php-mysql-replication#configuration).
+Also, if script crashes you need to save from time to time position form binlog (or gtid) to start from this position when you run this script again to avoid
+duplicates.
 
 3. ### I need to process 1 000 000 records and its taking forever!!
  Like I mention in 1 point use queue system like rabbitmq, redis or kafka, they will give you ability to process data in multiple scripts.
@@ -406,15 +419,22 @@ Also if script crashes you need to save from time to time position form binlog (
 4. ### I have a problem ? you script is missing something ! I have found a bug !
  Create an [issue](https://github.com/krowinski/php-mysql-replication/issues) I will try to work on it in my free time :)
 
-5. ### How much its give overhead to mysql server ?
- It work like any other mysql in slave mode and its giving same overhead.
+5. ### How much its give overhead to MYSQL server ?
+
+It work like any other MYSQL in slave mode and its giving same overhead.
  
 6. ### Socket timeouts error
- To fix this best is to increase db configurations "net_read_timeout" and "net_write_timeout" to 3600.  (tx Bijimon)
+
+To fix this best is to increase db configurations ```net_read_timeout``` and ```net_write_timeout``` to 3600.  (tx Bijimon)
  
 7. ### Partial updates fix
- Set in my.conf ```binlog_row_image=full``` to fix reciving only partial updates.  
- 
+
+Set in my.conf ```binlog_row_image=full``` to fix receiving only partial updates.
+
 8. ### No replication events when connected to replica server   
- Set in my.conf ```log_slave_updates=on``` to fix this (#71)(#66)
+Set in my.conf ```log_slave_updates=on``` to fix this (#71)(#66)
 
+9. ### "Big" updates / inserts
+Default MYSQL setting generates one big blob of stream this require more RAM/CPU you can change this for smaller stream using
+variable ```binlog_row_event_max_size``` [https://dev.mysql.com/doc/refman/8.0/en/replication-options-binary-log.html#sysvar_binlog_row_event_max_size]  to
+split into smaller chunks 
diff --git a/composer.json b/composer.json
index 2e7cc69f..81968e59 100644
--- a/composer.json
+++ b/composer.json
@@ -12,18 +12,23 @@
   ],
   "type": "library",
   "require": {
-    "php": ">=7.3",
+    "php": ">=8.2",
     "ext-bcmath": "*",
     "ext-json": "*",
     "ext-sockets": "*",
-    "doctrine/collections": "^1.3",
-    "doctrine/dbal": "^3.0",
-    "psr/simple-cache": "^1.0",
-    "symfony/dependency-injection": "^3.1|^4.0|^5.0",
-    "symfony/event-dispatcher": "^3.1|^4.0|^5.0"
+    "doctrine/collections": "^2.1",
+    "doctrine/dbal": "^3.8",
+    "psr/log": "^3.0",
+    "psr/simple-cache": "^3.0",
+    "symfony/dependency-injection": "^7.0",
+    "symfony/event-dispatcher": "^7.0"
   },
   "require-dev": {
-    "phpunit/phpunit": "^9.0"
+    "kubawerlos/php-cs-fixer-custom-fixers": "^3.19",
+    "monolog/monolog": "^3.5",
+    "phpstan/phpstan": "^1.10",
+    "phpunit/phpunit": "^10.5",
+    "symplify/easy-coding-standard": "^12.1"
   },
   "license": "MIT",
   "authors": [
@@ -45,5 +50,14 @@
   "minimum-stability": "stable",
   "config": {
     "sort-packages": true
+  },
+  "scripts": {
+    "cs:check": "ecs check",
+    "cs:fix": "ecs check --fix",
+    "phpstan:analyse": "phpstan analyse -cphpstan.neon",
+    "sa": [
+      "@cs:check",
+      "@phpstan:analyse"
+    ]
   }
 }
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..2bc77649
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,22 @@
+services:
+  replication-test-mysql-percona:
+    container_name: replication-test-mysql-percona
+    hostname: replication-test-mysql-percona
+    image: mysql:8.0
+    platform: linux/amd64
+    command: [
+      '--character-set-server=utf8mb4',
+      '--collation-server=utf8mb4_unicode_ci',
+    #  '--default-authentication-plugin=caching_sha2_password',
+     #'--default-authentication-plugin=mysql_native_password',
+      '--log_bin=binlog',
+      '--max_binlog_size=8M',
+      '--binlog_format=row',
+      '--server-id=1'
+    ]
+    environment:
+      - MYSQL_ROOT_PASSWORD=root
+      - MYSQL_DATABASE=mysqlreplication_test
+    ports:
+      - "3306:3306/tcp"
+    restart: unless-stopped
diff --git a/ecs.php b/ecs.php
new file mode 100644
index 00000000..7674cd0f
--- /dev/null
+++ b/ecs.php
@@ -0,0 +1,50 @@
+<?php
+
+declare(strict_types=1);
+
+use PhpCsFixer\Fixer\ArrayNotation\ReturnToYieldFromFixer;
+use PhpCsFixer\Fixer\ArrayNotation\YieldFromArrayToYieldsFixer;
+use PhpCsFixer\Fixer\Casing\NativeTypeDeclarationCasingFixer;
+use PhpCsFixer\Fixer\FunctionNotation\PhpdocToParamTypeFixer;
+use PhpCsFixer\Fixer\FunctionNotation\PhpdocToPropertyTypeFixer;
+use PhpCsFixer\Fixer\FunctionNotation\PhpdocToReturnTypeFixer;
+use PhpCsFixer\Fixer\Phpdoc\PhpdocAddMissingParamAnnotationFixer;
+use PhpCsFixer\Fixer\Whitespace\TypeDeclarationSpacesFixer;
+use PhpCsFixerCustomFixers\Fixer\NoLeadingSlashInGlobalNamespaceFixer;
+use PhpCsFixerCustomFixers\Fixer\PhpdocNoSuperfluousParamFixer;
+use PhpCsFixerCustomFixers\Fixer\PromotedConstructorPropertyFixer;
+use Symplify\EasyCodingStandard\Config\ECSConfig;
+use Symplify\EasyCodingStandard\ValueObject\Set\SetList;
+
+return static function (ECSConfig $ecsConfig): void {
+    $ecsConfig->parallel();
+    $ecsConfig->sets([
+        SetList::PSR_12,
+        SetList::CLEAN_CODE,
+        SetList::STRICT,
+        SetList::ARRAY,
+        SetList::PHPUNIT,
+        SetList::DOCTRINE_ANNOTATIONS,
+        SetList::COMMENTS,
+        SetList::SYMPLIFY,
+        SetList::CONTROL_STRUCTURES,
+    ]);
+
+    $ecsConfig->rules([
+        NativeTypeDeclarationCasingFixer::class,
+        ReturnToYieldFromFixer::class,
+        TypeDeclarationSpacesFixer::class,
+        YieldFromArrayToYieldsFixer::class,
+        PhpdocToPropertyTypeFixer::class,
+        PhpdocToParamTypeFixer::class,
+        PhpdocToReturnTypeFixer::class,
+        PromotedConstructorPropertyFixer::class,
+        NoLeadingSlashInGlobalNamespaceFixer::class,
+        PhpdocNoSuperfluousParamFixer::class,
+        PhpdocAddMissingParamAnnotationFixer::class,
+    ]);
+
+    $ecsConfig->fileExtensions(['php']);
+    $ecsConfig->cacheDirectory('.cache/ecs');
+    $ecsConfig->paths([__DIR__ . '/src', __DIR__ . '/tests', __DIR__ . '/example',]);
+};
diff --git a/example/benchmark.php b/example/benchmark.php
index 34a90292..df528690 100644
--- a/example/benchmark.php
+++ b/example/benchmark.php
@@ -1,5 +1,9 @@
 <?php
+
+/** @noinspection PhpComposerExtensionStubsInspection */
+
 declare(strict_types=1);
+
 error_reporting(E_ALL);
 date_default_timezone_set('UTC');
 include __DIR__ . '/../vendor/autoload.php';
@@ -23,19 +27,19 @@ class benchmark
     private const DB_HOST = '127.0.0.1';
     private const DB_PORT = 3306;
 
-    private $binLogStream;
+    private MySQLReplicationFactory $binLogStream;
 
     public function __construct()
     {
         $conn = $this->getConnection();
-        $conn->exec('DROP DATABASE IF EXISTS ' . self::DB_NAME);
-        $conn->exec('CREATE DATABASE ' . self::DB_NAME);
-        $conn->exec('USE ' . self::DB_NAME);
-        $conn->exec('CREATE TABLE test (i INT) ENGINE = MEMORY');
-        $conn->exec('INSERT INTO test VALUES(1)');
-        $conn->exec('CREATE TABLE test2 (i INT) ENGINE = MEMORY');
-        $conn->exec('INSERT INTO test2 VALUES(1)');
-        $conn->exec('RESET MASTER');
+        $conn->executeStatement('DROP DATABASE IF EXISTS ' . self::DB_NAME);
+        $conn->executeStatement('CREATE DATABASE ' . self::DB_NAME);
+        $conn->executeStatement('USE ' . self::DB_NAME);
+        $conn->executeStatement('CREATE TABLE test (i INT) ENGINE = MEMORY');
+        $conn->executeStatement('INSERT INTO test VALUES(1)');
+        $conn->executeStatement('CREATE TABLE test2 (i INT) ENGINE = MEMORY');
+        $conn->executeStatement('INSERT INTO test2 VALUES(1)');
+        $conn->executeStatement('RESET MASTER');
 
         $this->binLogStream = new MySQLReplicationFactory(
             (new ConfigBuilder())
@@ -43,17 +47,23 @@ public function __construct()
                 ->withPassword(self::DB_PASS)
                 ->withHost(self::DB_HOST)
                 ->withPort(self::DB_PORT)
-                ->withEventsOnly([ConstEventType::UPDATE_ROWS_EVENT_V2])
+                ->withEventsOnly(
+                    [
+                        ConstEventType::UPDATE_ROWS_EVENT_V2->value,
+                        // for mariadb v1
+                        ConstEventType::UPDATE_ROWS_EVENT_V1->value,
+                    ]
+                )
                 ->withSlaveId(9999)
                 ->withDatabasesOnly([self::DB_NAME])
                 ->build()
         );
 
         $this->binLogStream->registerSubscriber(
-            new  class extends EventSubscribers
-            {
-                private $start;
-                private $counter = 0;
+            new class() extends EventSubscribers {
+                private float $start;
+
+                private int $counter = 0;
 
                 public function __construct()
                 {
@@ -64,27 +74,15 @@ public function onUpdate(UpdateRowsDTO $event): void
                 {
                     ++$this->counter;
                     if (0 === ($this->counter % 1000)) {
-                        echo ((int)($this->counter / (microtime(true) - $this->start)) . ' event by seconds (' . $this->counter . ' total)') . PHP_EOL;
+                        echo ((int)($this->counter / (microtime(
+                            true
+                        ) - $this->start)) . ' event by seconds (' . $this->counter . ' total)') . PHP_EOL;
                     }
                 }
             }
         );
     }
 
-    private function getConnection(): Connection
-    {
-        return DriverManager::getConnection(
-            [
-                'user' => self::DB_USER,
-                'password' => self::DB_PASS,
-                'host' => self::DB_HOST,
-                'port' => self::DB_PORT,
-                'driver' => 'pdo_mysql',
-                'dbname' => self::DB_NAME
-            ]
-        );
-    }
-
     public function run(): void
     {
         $pid = pcntl_fork();
@@ -100,6 +98,20 @@ public function run(): void
         }
     }
 
+    private function getConnection(): Connection
+    {
+        return DriverManager::getConnection(
+            [
+                'user' => self::DB_USER,
+                'password' => self::DB_PASS,
+                'host' => self::DB_HOST,
+                'port' => self::DB_PORT,
+                'driver' => 'pdo_mysql',
+                'dbname' => self::DB_NAME,
+            ]
+        );
+    }
+
     private function consume(): void
     {
         $this->binLogStream->run();
@@ -110,9 +122,11 @@ private function produce(): void
         $conn = $this->getConnection();
 
         echo 'Start insert data' . PHP_EOL;
+
+        /** @phpstan-ignore-next-line */
         while (1) {
-            $conn->exec('UPDATE  test SET i = i + 1;');
-            $conn->exec('UPDATE test2 SET i = i + 1;');
+            $conn->executeStatement('UPDATE  test SET i = i + 1;');
+            $conn->executeStatement('UPDATE test2 SET i = i + 1;');
         }
     }
 }
diff --git a/example/dump_events.php b/example/dump_events.php
index 08d2172b..e5c6506d 100644
--- a/example/dump_events.php
+++ b/example/dump_events.php
@@ -1,11 +1,15 @@
 <?php
+
 declare(strict_types=1);
 
 error_reporting(E_ALL);
 date_default_timezone_set('UTC');
 include __DIR__ . '/../vendor/autoload.php';
 
+use Monolog\Handler\StreamHandler;
+use Monolog\Logger;
 use MySQLReplication\Config\ConfigBuilder;
+use MySQLReplication\Definitions\ConstEventType;
 use MySQLReplication\Event\DTO\EventDTO;
 use MySQLReplication\Event\EventSubscribers;
 use MySQLReplication\MySQLReplicationFactory;
@@ -18,12 +22,13 @@
 $binLogStream = new MySQLReplicationFactory(
     (new ConfigBuilder())
         ->withUser('root')
-        ->withHost('127.0.0.1')
+        ->withHost('0.0.0.0')
         ->withPassword('root')
         ->withPort(3306)
-        ->withSlaveId(100)
-        ->withHeartbeatPeriod(2)
-        ->build()
+        ->withHeartbeatPeriod(60)
+        ->withEventsIgnore([ConstEventType::HEARTBEAT_LOG_EVENT->value])
+        ->build(),
+    logger: new Logger('replicator', [new StreamHandler(STDOUT)])
 );
 
 /**
@@ -31,15 +36,14 @@
  * @see EventSubscribers
  */
 $binLogStream->registerSubscriber(
-    new class() extends EventSubscribers
-    {
+    new class() extends EventSubscribers {
         public function allEvents(EventDTO $event): void
         {
             // all events got __toString() implementation
-            echo $event;
+            #echo $event;
 
             // all events got JsonSerializable implementation
-            //echo json_encode($event, JSON_PRETTY_PRINT);
+            echo json_encode($event, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT);
 
             echo 'Memory usage ' . round(memory_get_usage() / 1048576, 2) . ' MB' . PHP_EOL;
         }
diff --git a/example/resuming.php b/example/resuming.php
index eb11c82a..ea252564 100644
--- a/example/resuming.php
+++ b/example/resuming.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace example;
@@ -25,10 +26,6 @@
         ->build()
 );
 
-/**
- * Class BenchmarkEventSubscribers
- * @package example
- */
 class MyEventSubscribers extends EventSubscribers
 {
     /**
@@ -45,38 +42,16 @@ public function allEvents(EventDTO $event): void
         echo 'Memory usage ' . round(memory_get_usage() / 1048576, 2) . ' MB' . PHP_EOL;
 
         // save event for resuming it later
-        BinLogBootstrap::save($event->getEventInfo()->getBinLogCurrent());
+        BinLogBootstrap::save($event->getEventInfo()->binLogCurrent);
     }
 }
 
-/**
- * Class SaveBinLogPos
- * @package example
- */
 class BinLogBootstrap
 {
-    /**
-     * @var string
-     */
-    private static $fileAndPath;
+    private static ?string $fileAndPath = null;
 
-    /**
-     * @return string
-     */
-    private static function getFileAndPath(): string
-    {
-        if (null === self::$fileAndPath) {
-            self::$fileAndPath = sys_get_temp_dir() . '/bin-log-replicator-last-position';
-        }
-        return self::$fileAndPath;
-    }
-
-    /**
-     * @param BinLogCurrent $binLogCurrent
-     */
     public static function save(BinLogCurrent $binLogCurrent): void
     {
-
         echo 'saving file:' . $binLogCurrent->getBinFileName() . ', position:' . $binLogCurrent->getBinLogPosition() . ' bin log position' . PHP_EOL;
 
         // can be redis/nosql/file - something fast!
@@ -85,10 +60,6 @@ public static function save(BinLogCurrent $binLogCurrent): void
         file_put_contents(self::getFileAndPath(), serialize($binLogCurrent));
     }
 
-    /**
-     * @param ConfigBuilder $builder
-     * @return ConfigBuilder
-     */
     public static function startFromPosition(ConfigBuilder $builder): ConfigBuilder
     {
         if (!is_file(self::getFileAndPath())) {
@@ -104,6 +75,14 @@ public static function startFromPosition(ConfigBuilder $builder): ConfigBuilder
             ->withBinLogFileName($binLogCurrent->getBinFileName())
             ->withBinLogPosition($binLogCurrent->getBinLogPosition());
     }
+
+    private static function getFileAndPath(): string
+    {
+        if (self::$fileAndPath === null) {
+            self::$fileAndPath = sys_get_temp_dir() . '/bin-log-replicator-last-position';
+        }
+        return self::$fileAndPath;
+    }
 }
 
 // register your events handler here
@@ -111,4 +90,3 @@ public static function startFromPosition(ConfigBuilder $builder): ConfigBuilder
 
 // start consuming events
 $binLogStream->run();
-
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 00000000..5938aa8e
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,6 @@
+parameters:
+  level: 9
+  checkMissingIterableValueType: false
+  tmpDir: .cache/phpstan/
+  paths:
+    - src
diff --git a/phpunit.xml b/phpunit.xml
index 964dde4e..df383901 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,13 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<phpunit backupGlobals="false"
-         backupStaticAttributes="false"
-         bootstrap="tests/bootstrap.php"
-         colors="true"
-         convertErrorsToExceptions="true"
-         convertNoticesToExceptions="true"
-         convertWarningsToExceptions="true"
-         processIsolation="false"
-         stopOnFailure="false">
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" bootstrap="tests/bootstrap.php" colors="true" processIsolation="false"
+         stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" cacheDirectory=".cache/phpunit"
+         backupStaticProperties="false">
     <testsuites>
         <testsuite name="Integration Suite">
             <directory>./tests/Integration</directory>
@@ -16,9 +10,9 @@
             <directory>./tests/Unit</directory>
         </testsuite>
     </testsuites>
-    <filter>
-        <whitelist>
+    <source>
+        <include>
             <directory suffix=".php">src/</directory>
-        </whitelist>
-    </filter>
-</phpunit>
\ No newline at end of file
+        </include>
+    </source>
+</phpunit>
diff --git a/src/MySQLReplication/BinLog/BinLogAuthPluginMode.php b/src/MySQLReplication/BinLog/BinLogAuthPluginMode.php
new file mode 100644
index 00000000..5ef6d2c4
--- /dev/null
+++ b/src/MySQLReplication/BinLog/BinLogAuthPluginMode.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace MySQLReplication\BinLog;
+
+enum BinLogAuthPluginMode: string
+{
+    case MysqlNativePassword = 'mysql_native_password';
+    case CachingSha2Password = 'caching_sha2_password';
+}
diff --git a/src/MySQLReplication/BinLog/BinLogCurrent.php b/src/MySQLReplication/BinLog/BinLogCurrent.php
index 1db18473..7a595110 100644
--- a/src/MySQLReplication/BinLog/BinLogCurrent.php
+++ b/src/MySQLReplication/BinLog/BinLogCurrent.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\BinLog;
@@ -7,29 +8,20 @@
 
 class BinLogCurrent implements JsonSerializable
 {
-    /**
-     * @var int
-     */
-    private $binLogPosition;
-    /**
-     * @var string
-     */
-    private $binFileName;
-    /**
-     * @var string
-     */
-    private $gtid;
-    /**
-     * @var string
-     */
-    private $mariaDbGtid;
-
-    public function getBinLogPosition(): int
+    private string $binLogPosition;
+
+    private string $binFileName;
+
+    private string $gtid;
+
+    private string $mariaDbGtid;
+
+    public function getBinLogPosition(): string
     {
         return $this->binLogPosition;
     }
 
-    public function setBinLogPosition(int $binLogPosition): void
+    public function setBinLogPosition(string $binLogPosition): void
     {
         $this->binLogPosition = $binLogPosition;
     }
@@ -64,8 +56,8 @@ public function setMariaDbGtid(string $mariaDbGtid): void
         $this->mariaDbGtid = $mariaDbGtid;
     }
 
-    public function jsonSerialize()
+    public function jsonSerialize(): array
     {
         return get_object_vars($this);
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/BinLog/BinLogException.php b/src/MySQLReplication/BinLog/BinLogException.php
index d99819ff..41718b2b 100644
--- a/src/MySQLReplication/BinLog/BinLogException.php
+++ b/src/MySQLReplication/BinLog/BinLogException.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\BinLog;
@@ -7,4 +8,4 @@
 
 class BinLogException extends MySQLReplicationException
 {
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/BinLog/BinLogServerInfo.php b/src/MySQLReplication/BinLog/BinLogServerInfo.php
index 80ae2993..962303be 100644
--- a/src/MySQLReplication/BinLog/BinLogServerInfo.php
+++ b/src/MySQLReplication/BinLog/BinLogServerInfo.php
@@ -1,42 +1,55 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\BinLog;
 
-class BinLogServerInfo
+use MySQLReplication\BinaryDataReader\BinaryDataReader;
+
+readonly class BinLogServerInfo
 {
     private const MYSQL_VERSION_MARIADB = 'MariaDB';
     private const MYSQL_VERSION_PERCONA = 'Percona';
     private const MYSQL_VERSION_GENERIC = 'MySQL';
-    private static $serverInfo = [];
 
-    public static function parsePackage(string $data, string $version): void
+    public function __construct(
+        public int $protocolVersion,
+        public string $serverVersion,
+        public int $connectionId,
+        public string $salt,
+        public ?BinLogAuthPluginMode $authPlugin,
+        public string $versionName,
+        public float $versionRevision
+    ) {
+    }
+
+    public static function make(string $data, string $version): self
     {
         $i = 0;
         $length = strlen($data);
-        self::$serverInfo['protocol_version'] = ord($data[$i]);
+        $protocolVersion = ord($data[$i]);
         ++$i;
 
         //version
-        self::$serverInfo['server_version'] = '';
+        $serverVersion = '';
         $start = $i;
         for ($i = $start; $i < $length; ++$i) {
             if ($data[$i] === chr(0)) {
                 ++$i;
                 break;
             }
-            self::$serverInfo['server_version'] .= $data[$i];
+            $serverVersion .= $data[$i];
         }
 
         //connection_id 4 bytes
-        self::$serverInfo['connection_id'] = unpack('I', $data[$i] . $data[++$i] . $data[++$i] . $data[++$i])[1];
+        $connectionId = BinaryDataReader::unpack('I', $data[$i] . $data[++$i] . $data[++$i] . $data[++$i])[1];
         ++$i;
 
         //auth_plugin_data_part_1
         //[len=8] first 8 bytes of the auth-plugin data
-        self::$serverInfo['salt'] = '';
+        $salt = '';
         for ($j = $i; $j < $i + 8; ++$j) {
-            self::$serverInfo['salt'] .= $data[$j];
+            $salt .= $data[$j];
         }
         $i += 8;
 
@@ -47,7 +60,7 @@ public static function parsePackage(string $data, string $version): void
         $i += 2;
 
         //character_set (1) -- default server character-set, only the lower 8-bits Protocol::CharacterSet (optional)
-        self::$serverInfo['character_set'] = $data[$i];
+        $characterSet = $data[$i];
         ++$i;
 
         //status_flags (2) -- Protocol::StatusFlags (optional)
@@ -57,33 +70,49 @@ public static function parsePackage(string $data, string $version): void
         $i += 2;
 
         //auth_plugin_data_len (1) -- length of the combined auth_plugin_data, if auth_plugin_data_len is > 0
-        $salt_len = ord($data[$i]);
+        $saltLen = ord($data[$i]);
         ++$i;
 
-        $salt_len = max(12, $salt_len - 9);
+        $saltLen = max(12, $saltLen - 9);
 
         $i += 10;
 
         //next salt
-        if ($length >= $i + $salt_len) {
-            for ($j = $i; $j < $i + $salt_len; ++$j) {
-                self::$serverInfo['salt'] .= $data[$j];
+        if ($length >= $i + $saltLen) {
+            for ($j = $i; $j < $i + $saltLen; ++$j) {
+                $salt .= $data[$j];
             }
-
         }
-        self::$serverInfo['auth_plugin_name'] = '';
-        $i += $salt_len + 1;
+        $authPlugin = '';
+        $i += $saltLen + 1;
         for ($j = $i; $j < $length - 1; ++$j) {
-            self::$serverInfo['auth_plugin_name'] .= $data[$j];
+            $authPlugin .= $data[$j];
         }
 
-        self::$serverInfo['version_name'] = self::parseVersion($version);
-        self::$serverInfo['version_revision'] = self::parseRevision($version);
+        return new self(
+            $protocolVersion,
+            $serverVersion,
+            $connectionId,
+            $salt,
+            BinLogAuthPluginMode::tryFrom($authPlugin),
+            self::parseVersion($serverVersion),
+            self::parseRevision($version)
+        );
     }
 
-    public static function getSalt(): string
+    public function isMariaDb(): bool
     {
-        return self::$serverInfo['salt'];
+        return $this->versionName === self::MYSQL_VERSION_MARIADB;
+    }
+
+    public function isPercona(): bool
+    {
+        return $this->versionName === self::MYSQL_VERSION_PERCONA;
+    }
+
+    public function isGeneric(): bool
+    {
+        return $this->versionName === self::MYSQL_VERSION_GENERIC;
     }
 
     /**
@@ -91,11 +120,11 @@ public static function getSalt(): string
      */
     private static function parseVersion(string $version): string
     {
-        if ('' !== $version) {
-            if (false !== strpos($version, self::MYSQL_VERSION_MARIADB)) {
+        if ($version !== '') {
+            if (str_contains($version, self::MYSQL_VERSION_MARIADB)) {
                 return self::MYSQL_VERSION_MARIADB;
             }
-            if (false !== strpos($version, self::MYSQL_VERSION_PERCONA)) {
+            if (str_contains($version, self::MYSQL_VERSION_PERCONA)) {
                 return self::MYSQL_VERSION_PERCONA;
             }
         }
@@ -103,33 +132,8 @@ private static function parseVersion(string $version): string
         return self::MYSQL_VERSION_GENERIC;
     }
 
-    public static function getRevision(): float
-    {
-        return self::$serverInfo['version_revision'];
-    }
-
-    public static function getVersion(): string
-    {
-        return self::$serverInfo['version_name'];
-    }
-
-    public static function isMariaDb(): bool
-    {
-        return self::MYSQL_VERSION_MARIADB === self::getVersion();
-    }
-
-    public static function isPercona(): bool
-    {
-        return self::MYSQL_VERSION_PERCONA === self::getVersion();
-    }
-
-    public static function isGeneric(): bool
-    {
-        return self::MYSQL_VERSION_GENERIC === self::getVersion();
-    }
-
     private static function parseRevision(string $version): float
     {
         return (float)$version;
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/BinLog/BinLogSocketConnect.php b/src/MySQLReplication/BinLog/BinLogSocketConnect.php
index 5d47df1e..ccdbabab 100644
--- a/src/MySQLReplication/BinLog/BinLogSocketConnect.php
+++ b/src/MySQLReplication/BinLog/BinLogSocketConnect.php
@@ -1,77 +1,82 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\BinLog;
 
 use MySQLReplication\BinaryDataReader\BinaryDataReader;
 use MySQLReplication\Config\Config;
+use MySQLReplication\Exception\MySQLReplicationException;
 use MySQLReplication\Gtid\GtidCollection;
-use MySQLReplication\Gtid\GtidException;
 use MySQLReplication\Repository\RepositoryInterface;
-use MySQLReplication\Socket\SocketException;
 use MySQLReplication\Socket\SocketInterface;
+use Psr\Log\LoggerInterface;
 
 class BinLogSocketConnect
 {
     private const COM_BINLOG_DUMP = 0x12;
     private const COM_REGISTER_SLAVE = 0x15;
     private const COM_BINLOG_DUMP_GTID = 0x1e;
-
     /**
-     * http://dev.mysql.com/doc/internals/en/auth-phase-fast-path.html 00 FE
+     * https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase.html 00 FE
      */
-    private $packageOkHeader = [0, 254];
-    private $binaryDataMaxLength = 16777215;
-    private $checkSum = false;
-
-    private $repository;
-    private $socket;
-    private $binLogCurrent;
+    private array $packageOkHeader = [0, 1, 254];
+    private int $binaryDataMaxLength = 16777215;
+    private bool $checkSum = false;
+    private BinLogCurrent $binLogCurrent;
+    private BinLogServerInfo $binLogServerInfo;
 
-    /**
-     * @throws BinLogException
-     * @throws GtidException
-     * @throws SocketException
-     */
     public function __construct(
-        RepositoryInterface $repository,
-        SocketInterface $socket
+        private readonly RepositoryInterface $repository,
+        private readonly SocketInterface $socket,
+        private readonly LoggerInterface $logger,
+        private readonly Config $config
     ) {
-        $this->repository = $repository;
-        $this->socket = $socket;
         $this->binLogCurrent = new BinLogCurrent();
 
-        $this->socket->connectToStream(Config::getHost(), Config::getPort());
-        BinLogServerInfo::parsePackage($this->getResponse(false), $this->repository->getVersion());
+        $this->socket->connectToStream($config->host, $config->port);
+
+        $this->logger->info('Connected to ' . $config->host . ':' . $config->port);
+
+        $this->binLogServerInfo = BinLogServerInfo::make(
+            $this->getResponse(false),
+            $this->repository->getVersion()
+        );
+
+        $this->logger->info(
+            'Server version name: ' . $this->binLogServerInfo->versionName . ', revision: ' . $this->binLogServerInfo->versionRevision
+        );
+
         $this->authenticate();
         $this->getBinlogStream();
     }
 
-    /**
-     * @throws BinLogException
-     * @throws SocketException
-     */
+    public function getBinLogServerInfo(): BinLogServerInfo
+    {
+        return $this->binLogServerInfo;
+    }
+
     public function getResponse(bool $checkResponse = true): string
     {
         $header = $this->socket->readFromSocket(4);
-        if ('' === $header) {
+        if ($header === '') {
             return '';
         }
-        $dataLength = unpack('L', $header[0] . $header[1] . $header[2] . chr(0))[1];
+        $dataLength = BinaryDataReader::unpack('L', $header[0] . $header[1] . $header[2] . chr(0))[1];
         $isMaxDataLength = $dataLength === $this->binaryDataMaxLength;
 
         $result = $this->socket->readFromSocket($dataLength);
-        if (true === $checkResponse) {
+        if ($checkResponse === true) {
             $this->isWriteSuccessful($result);
         }
 
         // https://dev.mysql.com/doc/internals/en/sending-more-than-16mbyte.html
         while ($isMaxDataLength) {
             $header = $this->socket->readFromSocket(4);
-            if ('' === $header) {
+            if ($header === '') {
                 return $result;
             }
-            $dataLength = unpack('L', $header[0] . $header[1] . $header[2] . chr(0))[1];
+            $dataLength = BinaryDataReader::unpack('L', $header[0] . $header[1] . $header[2] . chr(0))[1];
             $isMaxDataLength = $dataLength === $this->binaryDataMaxLength;
             $next_result = $this->socket->readFromSocket($dataLength);
             $result .= $next_result;
@@ -80,14 +85,21 @@ public function getResponse(bool $checkResponse = true): string
         return $result;
     }
 
-    /**
-     * @throws BinLogException
-     */
+    public function getBinLogCurrent(): BinLogCurrent
+    {
+        return $this->binLogCurrent;
+    }
+
+    public function getCheckSum(): bool
+    {
+        return $this->checkSum;
+    }
+
     private function isWriteSuccessful(string $data): void
     {
         $head = ord($data[0]);
         if (!in_array($head, $this->packageOkHeader, true)) {
-            $errorCode = unpack('v', $data[1] . $data[2])[1];
+            $errorCode = BinaryDataReader::unpack('v', $data[1] . $data[2])[1];
             $errorMessage = '';
             $packetLength = strlen($data);
             for ($i = 9; $i < $packetLength; ++$i) {
@@ -98,34 +110,61 @@ private function isWriteSuccessful(string $data): void
         }
     }
 
-    /**
-     * @throws BinLogException
-     * @throws SocketException
-     * @link http://dev.mysql.com/doc/internals/en/secure-password-authentication.html#packet-Authentication::Native41
-     */
     private function authenticate(): void
     {
+        if ($this->binLogServerInfo->authPlugin === null) {
+            throw new MySQLReplicationException(
+                MySQLReplicationException::BINLOG_AUTH_NOT_SUPPORTED,
+                MySQLReplicationException::BINLOG_AUTH_NOT_SUPPORTED_CODE
+            );
+        }
+
+        $this->logger->info(
+            'Trying to authenticate user: ' . $this->config->user . ' using ' . $this->binLogServerInfo->authPlugin->value . ' plugin'
+        );
+
         $data = pack('L', self::getCapabilities());
         $data .= pack('L', $this->binaryDataMaxLength);
         $data .= chr(33);
-        for ($i = 0; $i < 23; ++$i) {
-            $data .= chr(0);
+        $data .= str_repeat(chr(0), 23);
+        $data .= $this->config->user . chr(0);
+
+        $auth = '';
+        if ($this->binLogServerInfo->authPlugin === BinLogAuthPluginMode::MysqlNativePassword) {
+            $auth = $this->authenticateMysqlNativePasswordPlugin();
+        } elseif ($this->binLogServerInfo->authPlugin === BinLogAuthPluginMode::CachingSha2Password) {
+            $auth = $this->authenticateCachingSha2PasswordPlugin();
         }
-        $result = sha1(Config::getPassword(), true) ^ sha1(
-                BinLogServerInfo::getSalt() . sha1(sha1(Config::getPassword(), true), true), true
-            );
 
-        $data = $data . Config::getUser() . chr(0) . chr(strlen($result)) . $result;
+        $data .= chr(strlen($auth)) . $auth;
+        $data .= $this->binLogServerInfo->authPlugin->value . chr(0);
         $str = pack('L', strlen($data));
         $s = $str[0] . $str[1] . $str[2];
         $data = $s . chr(1) . $data;
 
         $this->socket->writeToSocket($data);
         $this->getResponse();
+
+        $this->logger->info('User authenticated');
+    }
+
+    private function authenticateCachingSha2PasswordPlugin(): string
+    {
+        $hash1 = hash('sha256', $this->config->password, true);
+        $hash2 = hash('sha256', $hash1, true);
+        $hash3 = hash('sha256', $hash2 . $this->binLogServerInfo->salt, true);
+        return $hash1 ^ $hash3;
+    }
+
+    private function authenticateMysqlNativePasswordPlugin(): string
+    {
+        $hash1 = sha1($this->config->password, true);
+        $hash2 = sha1($this->binLogServerInfo->salt . sha1(sha1($this->config->password, true), true), true);
+        return $hash1 ^ $hash2;
     }
 
     /**
-     * http://dev.mysql.com/doc/internals/en/capability-flags.html#packet-protocol::capabilityflags
+     * https://dev.mysql.com/doc/dev/mysql-server/latest/group__group__cs__capabilities__flags.html
      * https://github.com/siddontang/mixer/blob/master/doc/protocol.txt
      */
     private static function getCapabilities(): int
@@ -136,44 +175,46 @@ private static function getCapabilities(): int
         $transactions = 1 << 13;
         $secureConnection = 1 << 15;
         $protocol41 = 1 << 9;
+        $authPlugin = 1 << 19;
 
-        return ($longPassword | $longFlag | $transactions | $protocol41 | $secureConnection | $noSchema);
+        return $longPassword | $longFlag | $transactions | $protocol41 | $secureConnection | $noSchema | $authPlugin;
     }
 
-    /**
-     * @throws BinLogException
-     * @throws GtidException
-     * @throws SocketException
-     */
     private function getBinlogStream(): void
     {
         $this->checkSum = $this->repository->isCheckSum();
         if ($this->checkSum) {
-            $this->execute('SET @master_binlog_checksum = @@global.binlog_checksum');
+            $this->executeSQL('SET @master_binlog_checksum = @@global.binlog_checksum');
         }
 
-        if (0 !== Config::getHeartbeatPeriod()) {
+        if ($this->config->heartbeatPeriod > 0.00) {
             // master_heartbeat_period is in nanoseconds
-            $this->execute('SET @master_heartbeat_period = ' . Config::getHeartbeatPeriod() * 1000000000);
+            $this->executeSQL('SET @master_heartbeat_period = ' . $this->config->heartbeatPeriod * 1000000000);
+
+            $this->logger->info('Heartbeat period set to ' . $this->config->heartbeatPeriod . ' seconds');
+        }
+
+        if ($this->config->slaveUuid !== '') {
+            $this->executeSQL(
+                'SET @slave_uuid = \'' . $this->config->slaveUuid . '\', @replica_uuid = \'' . $this->config->slaveUuid . '\''
+            );
+
+            $this->logger->info('Salve uuid set to ' . $this->config->slaveUuid);
         }
 
         $this->registerSlave();
 
-        if ('' !== Config::getMariaDbGtid()) {
+        if ($this->config->mariaDbGtid !== '') {
             $this->setBinLogDumpMariaGtid();
         }
-        if ('' !== Config::getGtid()) {
+        if ($this->config->gtid !== '') {
             $this->setBinLogDumpGtid();
         } else {
             $this->setBinLogDump();
         }
     }
 
-    /**
-     * @throws BinLogException
-     * @throws SocketException
-     */
-    private function execute(string $sql): void
+    private function executeSQL(string $sql): void
     {
         $this->socket->writeToSocket(pack('LC', strlen($sql) + 1, 0x03) . $sql);
         $this->getResponse();
@@ -181,60 +222,52 @@ private function execute(string $sql): void
 
     /**
      * @see https://dev.mysql.com/doc/internals/en/com-register-slave.html
-     * @throws BinLogException
-     * @throws SocketException
      */
     private function registerSlave(): void
     {
-        $host = gethostname();
+        $host = (string)gethostname();
         $hostLength = strlen($host);
-        $userLength = strlen(Config::getUser());
-        $passLength = strlen(Config::getPassword());
+        $userLength = strlen($this->config->user);
+        $passLength = strlen($this->config->password);
 
         $data = pack('l', 18 + $hostLength + $userLength + $passLength);
         $data .= chr(self::COM_REGISTER_SLAVE);
-        $data .= pack('V', Config::getSlaveId());
+        $data .= pack('V', $this->config->slaveId);
         $data .= pack('C', $hostLength);
         $data .= $host;
         $data .= pack('C', $userLength);
-        $data .= Config::getUser();
+        $data .= $this->config->user;
         $data .= pack('C', $passLength);
-        $data .= Config::getPassword();
-        $data .= pack('v', Config::getPort());
+        $data .= $this->config->password;
+        $data .= pack('v', $this->config->port);
         $data .= pack('V', 0);
         $data .= pack('V', 0);
 
         $this->socket->writeToSocket($data);
         $this->getResponse();
+
+        $this->logger->info('Slave registered with id ' . $this->config->slaveId);
     }
 
-    /**
-     * @throws SocketException
-     * @throws BinLogException
-     */
     private function setBinLogDumpMariaGtid(): void
     {
-        $this->execute('SET @mariadb_slave_capability = 4');
-        $this->execute('SET @slave_connect_state = \'' . Config::getMariaDbGtid() . '\'');
-        $this->execute('SET @slave_gtid_strict_mode = 0');
-        $this->execute('SET @slave_gtid_ignore_duplicates = 0');
+        $this->executeSQL('SET @mariadb_slave_capability = 4');
+        $this->executeSQL('SET @slave_connect_state = \'' . $this->config->mariaDbGtid . '\'');
+        $this->executeSQL('SET @slave_gtid_strict_mode = 0');
+        $this->executeSQL('SET @slave_gtid_ignore_duplicates = 0');
+
+        $this->binLogCurrent->setMariaDbGtid($this->config->mariaDbGtid);
 
-        $this->binLogCurrent->setMariaDbGtid(Config::getMariaDbGtid());
+        $this->logger->info('Set Maria GTID to start from: ' . $this->config->mariaDbGtid);
     }
 
-    /**
-     * @see https://dev.mysql.com/doc/internals/en/com-binlog-dump-gtid.html
-     * @throws BinLogException
-     * @throws GtidException
-     * @throws SocketException
-     */
     private function setBinLogDumpGtid(): void
     {
-        $collection = GtidCollection::makeCollectionFromString(Config::getGtid());
+        $collection = GtidCollection::makeCollectionFromString($this->config->gtid);
 
         $data = pack('l', 26 + $collection->getEncodedLength()) . chr(self::COM_BINLOG_DUMP_GTID);
         $data .= pack('S', 0);
-        $data .= pack('I', Config::getSlaveId());
+        $data .= pack('I', $this->config->slaveId);
         $data .= pack('I', 3);
         $data .= chr(0);
         $data .= chr(0);
@@ -246,28 +279,33 @@ private function setBinLogDumpGtid(): void
         $this->socket->writeToSocket($data);
         $this->getResponse();
 
-        $this->binLogCurrent->setGtid(Config::getGtid());
+        $this->binLogCurrent->setGtid($this->config->gtid);
+
+        $this->logger->info('Set GTID to start from: ' . $this->config->gtid);
     }
 
     /**
-     * @see https://dev.mysql.com/doc/internals/en/com-binlog-dump.html
-     * @throws BinLogException
-     * @throws SocketException
+     * 1              [12] COM_BINLOG_DUMP
+     * 4              binlog-pos
+     * 2              flags
+     * 4              server-id
+     * string[EOF]    binlog-filename
      */
     private function setBinLogDump(): void
     {
-        $binFilePos = Config::getBinLogPosition();
-        $binFileName = Config::getBinLogFileName();
-        if (0 === $binFilePos && '' === $binFileName) {
+        $binFilePos = $this->config->binLogPosition;
+        $binFileName = $this->config->binLogFileName;
+        // if not set start from newest binlog
+        if ($binFilePos === '' && $binFileName === '') {
             $masterStatusDTO = $this->repository->getMasterStatus();
-            $binFilePos = $masterStatusDTO->getPosition();
-            $binFileName = $masterStatusDTO->getFile();
+            $binFilePos = $masterStatusDTO->position;
+            $binFileName = $masterStatusDTO->file;
         }
 
         $data = pack('i', strlen($binFileName) + 11) . chr(self::COM_BINLOG_DUMP);
         $data .= pack('I', $binFilePos);
         $data .= pack('v', 0);
-        $data .= pack('I', Config::getSlaveId());
+        $data .= pack('I', $this->config->slaveId);
         $data .= $binFileName;
 
         $this->socket->writeToSocket($data);
@@ -275,15 +313,7 @@ private function setBinLogDump(): void
 
         $this->binLogCurrent->setBinLogPosition($binFilePos);
         $this->binLogCurrent->setBinFileName($binFileName);
-    }
 
-    public function getBinLogCurrent(): BinLogCurrent
-    {
-        return $this->binLogCurrent;
-    }
-
-    public function getCheckSum(): bool
-    {
-        return $this->checkSum;
+        $this->logger->info('Set binlog to start from: ' . $binFileName . ':' . $binFilePos);
     }
 }
diff --git a/src/MySQLReplication/BinaryDataReader/BinaryDataReader.php b/src/MySQLReplication/BinaryDataReader/BinaryDataReader.php
index c0066eaa..d854dd1f 100644
--- a/src/MySQLReplication/BinaryDataReader/BinaryDataReader.php
+++ b/src/MySQLReplication/BinaryDataReader/BinaryDataReader.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\BinaryDataReader;
@@ -6,38 +7,54 @@
 class BinaryDataReader
 {
     public const NULL_COLUMN = 251;
+
     public const UNSIGNED_CHAR_COLUMN = 251;
+
     public const UNSIGNED_SHORT_COLUMN = 252;
+
     public const UNSIGNED_INT24_COLUMN = 253;
+
     public const UNSIGNED_INT64_COLUMN = 254;
+
     public const UNSIGNED_CHAR_LENGTH = 1;
+
     public const UNSIGNED_SHORT_LENGTH = 2;
+
     public const UNSIGNED_INT24_LENGTH = 3;
+
     public const UNSIGNED_INT32_LENGTH = 4;
+
     public const UNSIGNED_FLOAT_LENGTH = 4;
+
     public const UNSIGNED_DOUBLE_LENGTH = 8;
+
     public const UNSIGNED_INT40_LENGTH = 5;
+
     public const UNSIGNED_INT48_LENGTH = 6;
+
     public const UNSIGNED_INT56_LENGTH = 7;
-    public const UNSIGNED_INT64_LENGTH = 8;
 
-    private $data;
+    public const UNSIGNED_INT64_LENGTH = 8;
 
-    /**
-     * @var int
-     */
-    private $readBytes = 0;
+    private int $readBytes = 0;
 
-    public function __construct(string $data)
-    {
-        $this->data = $data;
+    public function __construct(
+        private string $binaryData
+    ) {
     }
 
     public static function pack64bit(int $value): string
     {
         return pack(
-            'C8', ($value >> 0) & 0xFF, ($value >> 8) & 0xFF, ($value >> 16) & 0xFF, ($value >> 24) & 0xFF,
-            ($value >> 32) & 0xFF, ($value >> 40) & 0xFF, ($value >> 48) & 0xFF, ($value >> 56) & 0xFF
+            'C8',
+            ($value >> 0) & 0xFF,
+            ($value >> 8) & 0xFF,
+            ($value >> 16) & 0xFF,
+            ($value >> 24) & 0xFF,
+            ($value >> 32) & 0xFF,
+            ($value >> 40) & 0xFF,
+            ($value >> 48) & 0xFF,
+            ($value >> 56) & 0xFF
         );
     }
 
@@ -48,14 +65,14 @@ public function advance(int $length): void
 
     public function readInt16(): int
     {
-        return unpack('s', $this->read(self::UNSIGNED_SHORT_LENGTH))[1];
+        return self::unpack('s', $this->read(self::UNSIGNED_SHORT_LENGTH))[1];
     }
 
     public function read(int $length): string
     {
-        $return = substr($this->data, 0, $length);
+        $return = substr($this->binaryData, 0, $length);
         $this->readBytes += $length;
-        $this->data = substr($this->data, $length);
+        $this->binaryData = substr($this->binaryData, $length);
 
         return $return;
     }
@@ -63,12 +80,9 @@ public function read(int $length): string
     public function unread(string $data): void
     {
         $this->readBytes -= strlen($data);
-        $this->data = $data . $this->data;
+        $this->binaryData = $data . $this->binaryData;
     }
 
-    /**
-     * @throws BinaryDataReaderException
-     */
     public function readCodedBinary(): ?int
     {
         $c = ord($this->read(self::UNSIGNED_CHAR_LENGTH));
@@ -90,12 +104,12 @@ public function readCodedBinary(): ?int
 
     public function readUInt16(): int
     {
-        return unpack('v', $this->read(self::UNSIGNED_SHORT_LENGTH))[1];
+        return self::unpack('v', $this->read(self::UNSIGNED_SHORT_LENGTH))[1];
     }
 
     public function readUInt24(): int
     {
-        $data = unpack('C3', $this->read(self::UNSIGNED_INT24_LENGTH));
+        $data = self::unpack('C3', $this->read(self::UNSIGNED_INT24_LENGTH));
 
         return $data[1] + ($data[2] << 8) + ($data[3] << 16);
     }
@@ -107,14 +121,14 @@ public function readUInt64(): string
 
     public function unpackUInt64(string $binary): string
     {
-        $data = unpack('V*', $binary);
+        $data = self::unpack('V*', $binary);
 
         return bcadd((string)$data[1], bcmul((string)$data[2], bcpow('2', '32')));
     }
 
     public function readInt24(): int
     {
-        $data = unpack('C3', $this->read(self::UNSIGNED_INT24_LENGTH));
+        $data = self::unpack('C3', $this->read(self::UNSIGNED_INT24_LENGTH));
 
         $res = $data[1] | ($data[2] << 8) | ($data[3] << 16);
         if ($res >= 0x800000) {
@@ -126,22 +140,16 @@ public function readInt24(): int
 
     public function readInt64(): string
     {
-        $data = unpack('V*', $this->read(self::UNSIGNED_INT64_LENGTH));
+        $data = self::unpack('V*', $this->read(self::UNSIGNED_INT64_LENGTH));
 
         return bcadd((string)$data[1], (string)($data[2] << 32));
     }
 
-    /**
-     * @throws BinaryDataReaderException
-     */
     public function readLengthString(int $size): string
     {
         return $this->read($this->readUIntBySize($size));
     }
 
-    /**
-     * @throws BinaryDataReaderException
-     */
     public function readUIntBySize(int $size): int
     {
         if ($size === self::UNSIGNED_CHAR_LENGTH) {
@@ -171,41 +179,38 @@ public function readUIntBySize(int $size): int
 
     public function readUInt8(): int
     {
-        return unpack('C', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
+        return self::unpack('C', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
     }
 
     public function readUInt32(): int
     {
-        return unpack('I', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
+        return self::unpack('I', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
     }
 
     public function readUInt40(): int
     {
-        $data1 = unpack('C', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
-        $data2 = unpack('I', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
+        $data1 = self::unpack('C', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
+        $data2 = self::unpack('I', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
 
         return $data1 + ($data2 << 8);
     }
 
     public function readUInt48(): int
     {
-        $data = unpack('v3', $this->read(self::UNSIGNED_INT48_LENGTH));
+        $data = self::unpack('v3', $this->read(self::UNSIGNED_INT48_LENGTH));
 
         return $data[1] + ($data[2] << 16) + ($data[3] << 32);
     }
 
     public function readUInt56(): int
     {
-        $data1 = unpack('C', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
-        $data2 = unpack('S', $this->read(self::UNSIGNED_SHORT_LENGTH))[1];
-        $data3 = unpack('I', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
+        $data1 = self::unpack('C', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
+        $data2 = self::unpack('S', $this->read(self::UNSIGNED_SHORT_LENGTH))[1];
+        $data3 = self::unpack('I', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
 
         return $data1 + ($data2 << 8) + ($data3 << 24);
     }
 
-    /**
-     * @throws BinaryDataReaderException
-     */
     public function readIntBeBySize(int $size): int
     {
         if ($size === self::UNSIGNED_CHAR_LENGTH) {
@@ -229,50 +234,54 @@ public function readIntBeBySize(int $size): int
 
     public function readInt8(): int
     {
-        $re = unpack('c', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
+        $re = self::unpack('c', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
+
         return $re >= 0x80 ? $re - 0x100 : $re;
     }
 
     public function readInt16Be(): int
     {
-        $re = unpack('n', $this->read(self::UNSIGNED_SHORT_LENGTH))[1];
+        $re = self::unpack('n', $this->read(self::UNSIGNED_SHORT_LENGTH))[1];
+
         return $re >= 0x8000 ? $re - 0x10000 : $re;
     }
 
     public function readInt24Be(): int
     {
-        $data = unpack('C3', $this->read(self::UNSIGNED_INT24_LENGTH));
+        $data = self::unpack('C3', $this->read(self::UNSIGNED_INT24_LENGTH));
         $re = ($data[1] << 16) | ($data[2] << 8) | $data[3];
+
         return $re >= 0x800000 ? $re - 0x1000000 : $re;
     }
 
     public function readInt32Be(): int
     {
-        $re = unpack('N', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
+        $re = self::unpack('N', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
+
         return $re >= 0x80000000 ? $re - 0x100000000 : $re;
     }
 
     public function readInt40Be(): int
     {
-        $data1 = unpack('N', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
-        $data2 = unpack('C', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
+        $data1 = self::unpack('N', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
+        $data2 = self::unpack('C', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
 
         return $data2 + ($data1 << 8);
     }
 
     public function readInt32(): int
     {
-        return unpack('i', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
+        return self::unpack('i', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
     }
 
     public function readFloat(): float
     {
-        return unpack('f', $this->read(self::UNSIGNED_FLOAT_LENGTH))[1];
+        return self::unpack('f', $this->read(self::UNSIGNED_FLOAT_LENGTH))[1];
     }
 
     public function readDouble(): float
     {
-        return unpack('d', $this->read(self::UNSIGNED_DOUBLE_LENGTH))[1];
+        return self::unpack('d', $this->read(self::UNSIGNED_DOUBLE_LENGTH))[1];
     }
 
     public function readTableId(): string
@@ -287,12 +296,12 @@ public function isComplete(int $size): bool
 
     public function getBinaryDataLength(): int
     {
-        return strlen($this->data);
+        return strlen($this->binaryData);
     }
 
-    public function getData(): string
+    public function getBinaryData(): string
     {
-        return $this->data;
+        return $this->binaryData;
     }
 
     public function getBinarySlice(int $binary, int $start, int $size, int $binaryLength): int
@@ -307,4 +316,13 @@ public function getReadBytes(): int
     {
         return $this->readBytes;
     }
-}
\ No newline at end of file
+
+    public static function unpack(string $format, string $string): array
+    {
+        $unpacked = unpack($format, $string);
+        if ($unpacked) {
+            return $unpacked;
+        }
+        return [];
+    }
+}
diff --git a/src/MySQLReplication/BinaryDataReader/BinaryDataReaderException.php b/src/MySQLReplication/BinaryDataReader/BinaryDataReaderException.php
index 5f84394b..6f7fd464 100644
--- a/src/MySQLReplication/BinaryDataReader/BinaryDataReaderException.php
+++ b/src/MySQLReplication/BinaryDataReader/BinaryDataReaderException.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\BinaryDataReader;
@@ -7,4 +8,4 @@
 
 class BinaryDataReaderException extends MySQLReplicationException
 {
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Cache/ArrayCache.php b/src/MySQLReplication/Cache/ArrayCache.php
index 0c2e4178..4fb88dc3 100644
--- a/src/MySQLReplication/Cache/ArrayCache.php
+++ b/src/MySQLReplication/Cache/ArrayCache.php
@@ -1,34 +1,31 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Cache;
 
-use MySQLReplication\Config\Config;
+use DateInterval;
 use Psr\SimpleCache\CacheInterface;
 
 class ArrayCache implements CacheInterface
 {
-    private static $tableMapCache = [];
+    private static array $tableMapCache = [];
+
+    public function __construct(
+        private readonly int $tableCacheSize = 0
+    ) {
+    }
 
-    /**
-     * @inheritDoc
-     */
-    public function get($key, $default = null)
+    public function get(string $key, mixed $default = null): mixed
     {
         return $this->has($key) ? self::$tableMapCache[$key] : $default;
     }
 
-    /**
-     * @inheritDoc
-     */
     public function has($key): bool
     {
         return isset(self::$tableMapCache[$key]);
     }
 
-    /**
-     * @inheritDoc
-     */
     public function clear(): bool
     {
         self::$tableMapCache = [];
@@ -36,10 +33,7 @@ public function clear(): bool
         return true;
     }
 
-    /**
-     * @inheritDoc
-     */
-    public function getMultiple($keys, $default = null)
+    public function getMultiple(iterable $keys, mixed $default = null): iterable
     {
         $data = [];
         foreach ($keys as $key) {
@@ -48,13 +42,10 @@ public function getMultiple($keys, $default = null)
             }
         }
 
-        return [] !== $data ? $data : $default;
+        return $data !== [] ? $data : (array)$default;
     }
 
-    /**
-     * @inheritDoc
-     */
-    public function setMultiple($values, $ttl = null): bool
+    public function setMultiple(iterable $values, null|int|DateInterval $ttl = null): bool
     {
         foreach ($values as $key => $value) {
             $this->set($key, $value);
@@ -63,19 +54,11 @@ public function setMultiple($values, $ttl = null): bool
         return true;
     }
 
-    /**
-     * @inheritDoc
-     */
-    public function set($key, $value, $ttl = null): bool
+    public function set(string $key, mixed $value, null|int|DateInterval $ttl = null): bool
     {
         // automatically clear table cache to save memory
-        if (count(self::$tableMapCache) > Config::getTableCacheSize()) {
-            self::$tableMapCache = array_slice(
-                self::$tableMapCache,
-                (int)(Config::getTableCacheSize() / 2),
-                null,
-                true
-            );
+        if (count(self::$tableMapCache) > $this->tableCacheSize) {
+            self::$tableMapCache = array_slice(self::$tableMapCache, (int)($this->tableCacheSize / 2), null, true);
         }
 
         self::$tableMapCache[$key] = $value;
@@ -83,10 +66,7 @@ public function set($key, $value, $ttl = null): bool
         return true;
     }
 
-    /**
-     * @inheritDoc
-     */
-    public function deleteMultiple($keys): bool
+    public function deleteMultiple(iterable $keys): bool
     {
         foreach ($keys as $key) {
             $this->delete($key);
@@ -95,13 +75,10 @@ public function deleteMultiple($keys): bool
         return true;
     }
 
-    /**
-     * @inheritDoc
-     */
-    public function delete($key): bool
+    public function delete(string $key): bool
     {
         unset(self::$tableMapCache[$key]);
 
         return true;
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Config/Config.php b/src/MySQLReplication/Config/Config.php
index 251baaef..e37e65f3 100644
--- a/src/MySQLReplication/Config/Config.php
+++ b/src/MySQLReplication/Config/Config.php
@@ -1,227 +1,132 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Config;
 
 use JsonSerializable;
 
-class Config implements JsonSerializable
+readonly class Config implements JsonSerializable
 {
-    private static $user;
-    private static $host;
-    private static $port;
-    private static $password;
-    private static $charset;
-    private static $gtid;
-    private static $slaveId;
-    private static $binLogFileName;
-    private static $binLogPosition;
-    private static $eventsOnly;
-    private static $eventsIgnore;
-    private static $tablesOnly;
-    private static $databasesOnly;
-    private static $mariaDbGtid;
-    private static $tableCacheSize;
-    private static $custom;
-    private static $heartbeatPeriod;
-
     public function __construct(
-        string $user,
-        string $host,
-        int $port,
-        string $password,
-        string $charset,
-        string $gtid,
-        string $mariaGtid,
-        int $slaveId,
-        string $binLogFileName,
-        int $binLogPosition,
-        array $eventsOnly,
-        array $eventsIgnore,
-        array $tablesOnly,
-        array $databasesOnly,
-        int $tableCacheSize,
-        array $custom,
-        float $heartbeatPeriod
+        public string $user,
+        public string $host,
+        public int $port,
+        public string $password,
+        public string $charset,
+        public string $gtid,
+        public string $mariaDbGtid,
+        public int $slaveId,
+        public string $binLogFileName,
+        public string $binLogPosition,
+        public array $eventsOnly,
+        public array $eventsIgnore,
+        public array $tablesOnly,
+        public array $databasesOnly,
+        public int $tableCacheSize,
+        public array $custom,
+        public float $heartbeatPeriod,
+        public string $slaveUuid,
     ) {
-        self::$user = $user;
-        self::$host = $host;
-        self::$port = $port;
-        self::$password = $password;
-        self::$charset = $charset;
-        self::$gtid = $gtid;
-        self::$slaveId = $slaveId;
-        self::$binLogFileName = $binLogFileName;
-        self::$binLogPosition = $binLogPosition;
-        self::$eventsOnly = $eventsOnly;
-        self::$eventsIgnore = $eventsIgnore;
-        self::$tablesOnly = $tablesOnly;
-        self::$databasesOnly = $databasesOnly;
-        self::$mariaDbGtid = $mariaGtid;
-        self::$tableCacheSize = $tableCacheSize;
-        self::$custom = $custom;
-        self::$heartbeatPeriod = $heartbeatPeriod;
     }
 
-    /**
-     * @throws ConfigException
-     */
-    public static function validate(): void
+    public function validate(): void
     {
-        if (!empty(self::$host)) {
-            $ip = gethostbyname(self::$host);
-            if (false === filter_var($ip, FILTER_VALIDATE_IP)) {
+        if (!empty($this->host)) {
+            $ip = gethostbyname($this->host);
+            if (filter_var($ip, FILTER_VALIDATE_IP) === false) {
                 throw new ConfigException(ConfigException::IP_ERROR_MESSAGE, ConfigException::IP_ERROR_CODE);
             }
         }
-        if (!empty(self::$port) && false === filter_var(
-                self::$port, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]]
-            )) {
+        if (!empty($this->port) && filter_var(
+            $this->port,
+            FILTER_VALIDATE_INT,
+            [
+                'options' => [
+                    'min_range' => 0,
+                ],
+            ]
+        ) === false) {
             throw new ConfigException(ConfigException::PORT_ERROR_MESSAGE, ConfigException::PORT_ERROR_CODE);
         }
-        if (!empty(self::$gtid)) {
-            foreach (explode(',', self::$gtid) as $gtid) {
+        if (!empty($this->gtid)) {
+            foreach (explode(',', $this->gtid) as $gtid) {
                 if (!(bool)preg_match(
-                    '/^([0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12})((?::[0-9-]+)+)$/', $gtid, $matches
+                    '/^([0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12})((?::[0-9-]+)+)$/',
+                    $gtid,
+                    $matches
                 )) {
                     throw new ConfigException(ConfigException::GTID_ERROR_MESSAGE, ConfigException::GTID_ERROR_CODE);
                 }
             }
         }
-        if (!empty(self::$slaveId) && false === filter_var(
-                self::$slaveId, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]]
-            )) {
-            throw new ConfigException(ConfigException::SLAVE_ID_ERROR_MESSAGE, ConfigException::SLAVE_ID_ERROR_CODE);
+        if (!empty($this->slaveId) && filter_var(
+            $this->slaveId,
+            FILTER_VALIDATE_INT,
+            [
+                'options' => [
+                    'min_range' => 0,
+                ],
+            ]
+        ) === false) {
+            throw new ConfigException(
+                ConfigException::SLAVE_ID_ERROR_MESSAGE,
+                ConfigException::SLAVE_ID_ERROR_CODE
+            );
         }
-        if (false === filter_var(self::$binLogPosition, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]])) {
+        if (bccomp($this->binLogPosition, '0') === -1) {
             throw new ConfigException(
-                ConfigException::BIN_LOG_FILE_POSITION_ERROR_MESSAGE, ConfigException::BIN_LOG_FILE_POSITION_ERROR_CODE
+                ConfigException::BIN_LOG_FILE_POSITION_ERROR_MESSAGE,
+                ConfigException::BIN_LOG_FILE_POSITION_ERROR_CODE
             );
         }
-        if (false === filter_var(self::$tableCacheSize, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]])) {
+        if (filter_var($this->tableCacheSize, FILTER_VALIDATE_INT, [
+            'options' => [
+                'min_range' => 0,
+            ],
+        ]) === false) {
             throw new ConfigException(
-                ConfigException::TABLE_CACHE_SIZE_ERROR_MESSAGE, ConfigException::TABLE_CACHE_SIZE_ERROR_CODE
+                ConfigException::TABLE_CACHE_SIZE_ERROR_MESSAGE,
+                ConfigException::TABLE_CACHE_SIZE_ERROR_CODE
             );
         }
-        if (0.0 !== self::$heartbeatPeriod && false === (
-                self::$heartbeatPeriod >= 0.001 && self::$heartbeatPeriod <= 4294967.0
-            )) {
+        if ($this->heartbeatPeriod !== 0.0 && false === (
+            $this->heartbeatPeriod >= 0.001 && $this->heartbeatPeriod <= 4294967.0
+        )) {
             throw new ConfigException(
-                ConfigException::HEARTBEAT_PERIOD_ERROR_MESSAGE, ConfigException::HEARTBEAT_PERIOD_ERROR_CODE
+                ConfigException::HEARTBEAT_PERIOD_ERROR_MESSAGE,
+                ConfigException::HEARTBEAT_PERIOD_ERROR_CODE
             );
         }
     }
 
-    public static function getCustom(): array
-    {
-        return self::$custom;
-    }
-
-    public static function getUser(): string
-    {
-        return self::$user;
-    }
 
-    public static function getHost(): string
+    public function checkDataBasesOnly(string $database): bool
     {
-        return self::$host;
+        return $this->databasesOnly !== [] && !in_array($database, $this->databasesOnly, true);
     }
 
-    public static function getPort(): int
-    {
-        return self::$port;
-    }
-
-    public static function getPassword(): string
-    {
-        return self::$password;
-    }
 
-    public static function getCharset(): string
+    public function checkTablesOnly(string $table): bool
     {
-        return self::$charset;
+        return $this->tablesOnly !== [] && !in_array($table, $this->tablesOnly, true);
     }
 
-    public static function getGtid(): string
+    public function checkEvent(int $type): bool
     {
-        return self::$gtid;
-    }
-
-    public static function getMariaDbGtid(): string
-    {
-        return self::$mariaDbGtid;
-    }
-
-    public static function getSlaveId(): int
-    {
-        return self::$slaveId;
-    }
-
-    public static function getBinLogFileName(): string
-    {
-        return self::$binLogFileName;
-    }
-
-    public static function getBinLogPosition(): int
-    {
-        return self::$binLogPosition;
-    }
-
-    public static function getTableCacheSize(): int
-    {
-        return self::$tableCacheSize;
-    }
-
-    public static function checkDataBasesOnly(string $database): bool
-    {
-        return [] !== self::getDatabasesOnly() && !in_array($database, self::getDatabasesOnly(), true);
-    }
-
-    public static function getDatabasesOnly(): array
-    {
-        return self::$databasesOnly;
-    }
-
-    public static function checkTablesOnly(string $table): bool
-    {
-        return [] !== self::getTablesOnly() && !in_array($table, self::getTablesOnly(), true);
-    }
-
-    public static function getTablesOnly(): array
-    {
-        return self::$tablesOnly;
-    }
-
-    public static function checkEvent(int $type): bool
-    {
-        if ([] !== self::getEventsOnly() && !in_array($type, self::getEventsOnly(), true)) {
+        if ($this->eventsOnly !== [] && !in_array($type, $this->eventsOnly, true)) {
             return false;
         }
 
-        if (in_array($type, self::getEventsIgnore(), true)) {
+        if (in_array($type, $this->eventsIgnore, true)) {
             return false;
         }
 
         return true;
     }
 
-    public static function getEventsOnly(): array
-    {
-        return self::$eventsOnly;
-    }
-
-    public static function getEventsIgnore(): array
-    {
-        return self::$eventsIgnore;
-    }
-
-    public static function getHeartbeatPeriod(): float
-    {
-        return self::$heartbeatPeriod;
-    }
-
-    public function jsonSerialize()
+    public function jsonSerialize(): array
     {
         return get_class_vars(self::class);
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Config/ConfigBuilder.php b/src/MySQLReplication/Config/ConfigBuilder.php
index ce4b6f23..e86e9d98 100644
--- a/src/MySQLReplication/Config/ConfigBuilder.php
+++ b/src/MySQLReplication/Config/ConfigBuilder.php
@@ -1,27 +1,53 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Config;
 
 class ConfigBuilder
 {
-    private $user = '';
-    private $host = 'localhost';
-    private $port = 3306;
-    private $password = '';
-    private $charset = 'utf8';
-    private $gtid = '';
-    private $slaveId = 666;
-    private $binLogFileName = '';
-    private $binLogPosition = 0;
-    private $eventsOnly = [];
-    private $eventsIgnore = [];
-    private $tablesOnly = [];
-    private $databasesOnly = [];
-    private $mariaDbGtid = '';
-    private $tableCacheSize = 128;
-    private $custom = [];
-    private $heartbeatPeriod = 0.0;
+    private string $user = '';
+
+    private string $host = 'localhost';
+
+    private int $port = 3306;
+
+    private string $password = '';
+
+    private string $charset = 'utf8';
+
+    private string $gtid = '';
+
+    private int $slaveId = 666;
+
+    private string $binLogFileName = '';
+
+    private string $binLogPosition = '';
+
+    private array $eventsOnly = [];
+
+    private array $eventsIgnore = [];
+
+    private array $tablesOnly = [];
+
+    private array $databasesOnly = [];
+
+    private string $mariaDbGtid = '';
+
+    private int $tableCacheSize = 128;
+
+    private array $custom = [];
+
+    private float $heartbeatPeriod = 0.0;
+
+    private string $slaveUuid = '0015d2b6-8a06-4e5e-8c07-206ef3fbd274';
+
+    public function withSlaveUuid(string $slaveUuid): self
+    {
+        $this->slaveUuid = $slaveUuid;
+
+        return $this;
+    }
 
     public function withUser(string $user): self
     {
@@ -79,7 +105,7 @@ public function withBinLogFileName(string $binLogFileName): self
         return $this;
     }
 
-    public function withBinLogPosition(int $binLogPosition): self
+    public function withBinLogPosition(string $binLogPosition): self
     {
         $this->binLogPosition = $binLogPosition;
 
@@ -93,6 +119,9 @@ public function withEventsOnly(array $eventsOnly): self
         return $this;
     }
 
+    /**
+     * @param array<int, int> $eventsIgnore
+     */
     public function withEventsIgnore(array $eventsIgnore): self
     {
         $this->eventsIgnore = $eventsIgnore;
@@ -121,7 +150,6 @@ public function withMariaDbGtid(string $mariaDbGtid): self
         return $this;
     }
 
-
     public function withTableCacheSize(int $tableCacheSize): self
     {
         $this->tableCacheSize = $tableCacheSize;
@@ -129,7 +157,6 @@ public function withTableCacheSize(int $tableCacheSize): self
         return $this;
     }
 
-
     public function withCustom(array $custom): self
     {
         $this->custom = $custom;
@@ -166,7 +193,8 @@ public function build(): Config
             $this->databasesOnly,
             $this->tableCacheSize,
             $this->custom,
-            $this->heartbeatPeriod
+            $this->heartbeatPeriod,
+            $this->slaveUuid
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Config/ConfigException.php b/src/MySQLReplication/Config/ConfigException.php
index 90639b5b..0dc05eb3 100644
--- a/src/MySQLReplication/Config/ConfigException.php
+++ b/src/MySQLReplication/Config/ConfigException.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Config;
@@ -7,4 +8,18 @@
 
 class ConfigException extends MySQLReplicationException
 {
-}
\ No newline at end of file
+    public const TABLE_CACHE_SIZE_ERROR_MESSAGE = 'Table cache must be integer';
+    public const TABLE_CACHE_SIZE_ERROR_CODE = 411;
+    public const HEARTBEAT_PERIOD_ERROR_MESSAGE = 'Heartbeat period must be integer min:1 max:4294967';
+    public const HEARTBEAT_PERIOD_ERROR_CODE = 412;
+    public const BIN_LOG_FILE_POSITION_ERROR_MESSAGE = 'Incorrect binlog position';
+    public const BIN_LOG_FILE_POSITION_ERROR_CODE = 409;
+    public const SLAVE_ID_ERROR_MESSAGE = 'Incorrect slave id type';
+    public const SLAVE_ID_ERROR_CODE = 407;
+    public const GTID_ERROR_MESSAGE = 'Incorrect gtid';
+    public const GTID_ERROR_CODE = 406;
+    public const PORT_ERROR_MESSAGE = 'Incorrect port given should be numeric ';
+    public const PORT_ERROR_CODE = 402;
+    public const IP_ERROR_MESSAGE = 'Incorrect IP given';
+    public const IP_ERROR_CODE = 401;
+}
diff --git a/src/MySQLReplication/Definitions/ConstEventType.php b/src/MySQLReplication/Definitions/ConstEventType.php
index 1724a82d..2c06f027 100644
--- a/src/MySQLReplication/Definitions/ConstEventType.php
+++ b/src/MySQLReplication/Definitions/ConstEventType.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Definitions;
@@ -6,70 +7,68 @@
 /**
  * @see https://dev.mysql.com/doc/internals/en/event-classes-and-types.html
  */
-class ConstEventType
+enum ConstEventType: int
 {
-    public const UNKNOWN_EVENT = 0;
-    public const START_EVENT_V3 = 1;
-    public const QUERY_EVENT = 2;
-    public const STOP_EVENT = 3;
-    public const ROTATE_EVENT = 4;
-    public const INTVAR_EVENT = 5;
-    public const LOAD_EVENT = 6;
-    public const SLAVE_EVENT = 7;
-    public const CREATE_FILE_EVENT = 8;
-    public const APPEND_BLOCK_EVENT = 9;
-    public const EXEC_LOAD_EVENT = 10;
-    public const DELETE_FILE_EVENT = 11;
-    public const NEW_LOAD_EVENT = 12;
-    public const RAND_EVENT = 13;
-    public const USER_VAR_EVENT = 14;
-    public const FORMAT_DESCRIPTION_EVENT = 15;
+    case MARIA_GTID_LIST_EVENT = 163;
+    case INCIDENT_EVENT = 26;
+    case IGNORABLE_LOG_EVENT = 28;
+    case PRE_GA_UPDATE_ROWS_EVENT = 21;
+    case HEARTBEAT_LOG_EVENT = 27;
+    case GTID_LOG_EVENT = 33;
+    case QUERY_EVENT = 2;
+    case EXEC_LOAD_EVENT = 10;
+    case PRE_GA_DELETE_ROWS_EVENT = 22;
+    case MARIA_DELETE_ROWS_COMPRESSED_EVENT_V1 = 168;
+    case MARIA_WRITE_ROWS_COMPRESSED_EVENT = 169;
+    case DELETE_ROWS_EVENT_V2 = 32;
+    case MARIA_WRITE_ROWS_COMPRESSED_EVENT_V1 = 166;
+    case DELETE_ROWS_EVENT_V1 = 25;
+    case MARIA_QUERY_COMPRESSED_EVENT = 165;
+    case ROWS_QUERY_LOG_EVENT = 29;
+    case MARIA_UPDATE_ROWS_COMPRESSED_EVENT_V1 = 167;
+    case TABLE_MAP_EVENT = 19;
+    case MARIA_START_ENCRYPTION_EVENT = 164;
+    case MARIA_BINLOG_CHECKPOINT_EVENT = 161;
+    case RAND_EVENT = 13;
+    case XID_EVENT = 16;
+    case PREVIOUS_GTIDS_LOG_EVENT = 35;
+    case ROTATE_EVENT = 4;
+    case DELETE_FILE_EVENT = 11;
+    case BEGIN_LOAD_QUERY_EVENT = 17;
+    case START_EVENT_V3 = 1;
+    case SLAVE_EVENT = 7;
+    case CREATE_FILE_EVENT = 8;
+    case MARIA_DELETE_ROWS_COMPRESSED_EVENT = 171;
+    case UPDATE_ROWS_EVENT_V1 = 24;
+    case UPDATE_ROWS_EVENT_V2 = 31;
+    case LOAD_EVENT = 6;
+    case NEW_LOAD_EVENT = 12;
+    case USER_VAR_EVENT = 14;
+    case FORMAT_DESCRIPTION_EVENT = 15;
+    case EXECUTE_LOAD_QUERY_EVENT = 18;
+    case ANONYMOUS_GTID_LOG_EVENT = 34;
+    case WRITE_ROWS_EVENT_V1 = 23;
+    case WRITE_ROWS_EVENT_V2 = 30;
+    case PRE_GA_WRITE_ROWS_EVENT = 20;
+    case UNKNOWN_EVENT = 0;
+    case APPEND_BLOCK_EVENT = 9;
+    case STOP_EVENT = 3;
+    case INTVAR_EVENT = 5;
+    case MARIA_UPDATE_ROWS_COMPRESSED_EVENT = 170;
+    case MARIA_GTID_EVENT = 162;
 
     //Transaction ID for 2PC, written whenever a COMMIT is expected.
-    public const XID_EVENT = 16;
-    public const BEGIN_LOAD_QUERY_EVENT = 17;
-    public const EXECUTE_LOAD_QUERY_EVENT = 18;
-
-    public const GTID_LOG_EVENT = 33;
-    public const ANONYMOUS_GTID_LOG_EVENT = 34;
-    public const PREVIOUS_GTIDS_LOG_EVENT = 35;
-
-    public const INCIDENT_EVENT = 26;
-    public const HEARTBEAT_LOG_EVENT = 27;
-    public const IGNORABLE_LOG_EVENT = 28;
-    public const ROWS_QUERY_LOG_EVENT = 29;
 
     // Row-Based Binary Logging
     // TABLE_MAP_EVENT,WRITE_ROWS_EVENT
     // UPDATE_ROWS_EVENT,DELETE_ROWS_EVENT
-    public const TABLE_MAP_EVENT = 19;
 
     // MySQL 5.1.5 to 5.1.17,
-    public const PRE_GA_WRITE_ROWS_EVENT = 20;
-    public const PRE_GA_UPDATE_ROWS_EVENT = 21;
-    public const PRE_GA_DELETE_ROWS_EVENT = 22;
 
     // MySQL 5.1.15 to 5.6.x
-    public const WRITE_ROWS_EVENT_V1 = 23;
-    public const UPDATE_ROWS_EVENT_V1 = 24;
-    public const DELETE_ROWS_EVENT_V1 = 25;
 
     // MySQL 5.6.x
-    public const WRITE_ROWS_EVENT_V2 = 30;
-    public const UPDATE_ROWS_EVENT_V2 = 31;
-    public const DELETE_ROWS_EVENT_V2 = 32;
 
     // mariadb
     // https://github.com/MariaDB/server/blob/10.4/sql/log_event.h
-    public const MARIA_BINLOG_CHECKPOINT_EVENT = 161;
-    public const MARIA_GTID_EVENT = 162;
-    public const MARIA_GTID_LIST_EVENT = 163;
-    public const MARIA_START_ENCRYPTION_EVENT = 164;
-    public const MARIA_QUERY_COMPRESSED_EVENT = 165;
-    public const MARIA_WRITE_ROWS_COMPRESSED_EVENT_V1 = 166;
-    public const MARIA_UPDATE_ROWS_COMPRESSED_EVENT_V1 = 167;
-    public const MARIA_DELETE_ROWS_COMPRESSED_EVENT_V1 = 168;
-    public const MARIA_WRITE_ROWS_COMPRESSED_EVENT = 169;
-    public const MARIA_UPDATE_ROWS_COMPRESSED_EVENT = 170;
-    public const MARIA_DELETE_ROWS_COMPRESSED_EVENT = 171;
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Definitions/ConstEventsNames.php b/src/MySQLReplication/Definitions/ConstEventsNames.php
index 6706dbbf..75c3ad35 100644
--- a/src/MySQLReplication/Definitions/ConstEventsNames.php
+++ b/src/MySQLReplication/Definitions/ConstEventsNames.php
@@ -1,19 +1,20 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Definitions;
 
-class ConstEventsNames
+enum ConstEventsNames: string
 {
-    public const TABLE_MAP = 'tableMap';
-    public const GTID = 'gtid';
-    public const XID = 'xid';
-    public const QUERY = 'query';
-    public const ROTATE = 'rotate';
-    public const DELETE = 'delete';
-    public const UPDATE = 'update';
-    public const WRITE = 'write';
-    public const MARIADB_GTID = 'mariadb gtid';
-    public const FORMAT_DESCRIPTION = 'format description';
-    public const HEARTBEAT = 'heartbeat';
-}
\ No newline at end of file
+    case XID = 'xid';
+    case DELETE = 'delete';
+    case QUERY = 'query';
+    case ROTATE = 'rotate';
+    case GTID = 'gtid';
+    case MARIADB_GTID = 'mariadb gtid';
+    case UPDATE = 'update';
+    case HEARTBEAT = 'heartbeat';
+    case TABLE_MAP = 'tableMap';
+    case WRITE = 'write';
+    case FORMAT_DESCRIPTION = 'format description';
+}
diff --git a/src/MySQLReplication/Definitions/ConstFieldType.php b/src/MySQLReplication/Definitions/ConstFieldType.php
index 7c785a15..b79fce01 100644
--- a/src/MySQLReplication/Definitions/ConstFieldType.php
+++ b/src/MySQLReplication/Definitions/ConstFieldType.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Definitions;
@@ -36,9 +37,7 @@ class ConstFieldType
     public const VAR_STRING = 253;
     public const STRING = 254;
     public const GEOMETRY = 255;
-
-    public const CHAR = self::TINY;
-    public const INTERVAL = self::ENUM;
-
+    public const CHAR = 1;
+    public const INTERVAL = 247;
     public const IGNORE = 666;
 }
diff --git a/src/MySQLReplication/Event/DTO/DeleteRowsDTO.php b/src/MySQLReplication/Event/DTO/DeleteRowsDTO.php
index ffa2cab9..052e58b0 100644
--- a/src/MySQLReplication/Event/DTO/DeleteRowsDTO.php
+++ b/src/MySQLReplication/Event/DTO/DeleteRowsDTO.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\DTO;
@@ -7,10 +8,10 @@
 
 class DeleteRowsDTO extends RowsDTO
 {
-    protected $type = ConstEventsNames::DELETE;
+    protected ConstEventsNames $type = ConstEventsNames::DELETE;
 
     public function getType(): string
     {
-        return $this->type;
+        return $this->type->value;
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/DTO/EventDTO.php b/src/MySQLReplication/Event/DTO/EventDTO.php
index 5f9cce43..ea23d5d4 100644
--- a/src/MySQLReplication/Event/DTO/EventDTO.php
+++ b/src/MySQLReplication/Event/DTO/EventDTO.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\DTO;
@@ -12,20 +13,18 @@
  */
 abstract class EventDTO extends GenericEvent implements JsonSerializable
 {
-    protected $eventInfo;
-
     public function __construct(
-        EventInfo $eventInfo
+        protected EventInfo $eventInfo
     ) {
-        $this->eventInfo = $eventInfo;
+        parent::__construct();
     }
 
+    abstract public function __toString(): string;
+
     public function getEventInfo(): EventInfo
     {
         return $this->eventInfo;
     }
 
     abstract public function getType(): string;
-
-    abstract public function __toString(): string;
 }
diff --git a/src/MySQLReplication/Event/DTO/FormatDescriptionEventDTO.php b/src/MySQLReplication/Event/DTO/FormatDescriptionEventDTO.php
index 691c2e2b..1377d87f 100644
--- a/src/MySQLReplication/Event/DTO/FormatDescriptionEventDTO.php
+++ b/src/MySQLReplication/Event/DTO/FormatDescriptionEventDTO.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\DTO;
@@ -7,24 +8,24 @@
 
 class FormatDescriptionEventDTO extends EventDTO
 {
-    private $type = ConstEventsNames::FORMAT_DESCRIPTION;
-
-    public function getType(): string
-    {
-        return $this->type;
-    }
+    private ConstEventsNames $type = ConstEventsNames::FORMAT_DESCRIPTION;
 
     public function __toString(): string
     {
         return PHP_EOL .
             '=== Event ' . $this->getType() . ' === ' . PHP_EOL .
             'Date: ' . $this->eventInfo->getDateTime() . PHP_EOL .
-            'Log position: ' . $this->eventInfo->getPos() . PHP_EOL .
-            'Event size: ' . $this->eventInfo->getSize() . PHP_EOL;
+            'Log position: ' . $this->eventInfo->pos . PHP_EOL .
+            'Event size: ' . $this->eventInfo->size . PHP_EOL;
+    }
+
+    public function getType(): string
+    {
+        return $this->type->value;
     }
 
-    public function jsonSerialize()
+    public function jsonSerialize(): array
     {
         return get_object_vars($this);
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/DTO/GTIDLogDTO.php b/src/MySQLReplication/Event/DTO/GTIDLogDTO.php
index a816cc89..1a28eae5 100644
--- a/src/MySQLReplication/Event/DTO/GTIDLogDTO.php
+++ b/src/MySQLReplication/Event/DTO/GTIDLogDTO.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\DTO;
@@ -8,40 +9,14 @@
 
 class GTIDLogDTO extends EventDTO
 {
-    private $commit;
-    private $gtid;
-    private $type = ConstEventsNames::GTID;
-
-    /**
-     * GTIDLogEventDTO constructor.
-     * @param EventInfo $eventInfo
-     * @param bool $commit
-     * @param string $gtid
-     */
+    private ConstEventsNames $type = ConstEventsNames::GTID;
+
     public function __construct(
         EventInfo $eventInfo,
-        bool $commit,
-        string $gtid
+        public readonly bool $commit,
+        public readonly string $gtid
     ) {
         parent::__construct($eventInfo);
-
-        $this->commit = $commit;
-        $this->gtid = $gtid;
-    }
-
-    public function isCommit(): bool
-    {
-        return $this->commit;
-    }
-
-    public function getGtid(): string
-    {
-        return $this->gtid;
-    }
-
-    public function getType(): string
-    {
-        return $this->type;
     }
 
     public function __toString(): string
@@ -49,14 +24,19 @@ public function __toString(): string
         return PHP_EOL .
             '=== Event ' . $this->getType() . ' === ' . PHP_EOL .
             'Date: ' . $this->eventInfo->getDateTime() . PHP_EOL .
-            'Log position: ' . $this->eventInfo->getPos() . PHP_EOL .
-            'Event size: ' . $this->eventInfo->getSize() . PHP_EOL .
+            'Log position: ' . $this->eventInfo->pos . PHP_EOL .
+            'Event size: ' . $this->eventInfo->size . PHP_EOL .
             'Commit: ' . var_export($this->commit, true) . PHP_EOL .
             'GTID NEXT: ' . $this->gtid . PHP_EOL;
     }
 
-    public function jsonSerialize()
+    public function getType(): string
+    {
+        return $this->type->value;
+    }
+
+    public function jsonSerialize(): array
     {
         return get_object_vars($this);
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/DTO/HeartbeatDTO.php b/src/MySQLReplication/Event/DTO/HeartbeatDTO.php
index 520c10b9..bd9c168c 100644
--- a/src/MySQLReplication/Event/DTO/HeartbeatDTO.php
+++ b/src/MySQLReplication/Event/DTO/HeartbeatDTO.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\DTO;
@@ -7,24 +8,24 @@
 
 class HeartbeatDTO extends EventDTO
 {
-    protected $type = ConstEventsNames::HEARTBEAT;
+    protected ConstEventsNames $type = ConstEventsNames::HEARTBEAT;
 
     public function __toString(): string
     {
         return PHP_EOL .
             '=== Event ' . $this->getType() . ' === ' . PHP_EOL .
             'Date: ' . $this->eventInfo->getDateTime() . PHP_EOL .
-            'Log position: ' . $this->eventInfo->getPos() . PHP_EOL .
-            'Event size: ' . $this->eventInfo->getSize() . PHP_EOL;
+            'Log position: ' . $this->eventInfo->pos . PHP_EOL .
+            'Event size: ' . $this->eventInfo->size . PHP_EOL;
     }
 
     public function getType(): string
     {
-        return $this->type;
+        return $this->type->value;
     }
 
-    public function jsonSerialize()
+    public function jsonSerialize(): array
     {
         return get_object_vars($this);
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/DTO/MariaDbGtidLogDTO.php b/src/MySQLReplication/Event/DTO/MariaDbGtidLogDTO.php
index 375caf89..0d113245 100644
--- a/src/MySQLReplication/Event/DTO/MariaDbGtidLogDTO.php
+++ b/src/MySQLReplication/Event/DTO/MariaDbGtidLogDTO.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\DTO;
@@ -8,22 +9,15 @@
 
 class MariaDbGtidLogDTO extends EventDTO
 {
-    private $type = ConstEventsNames::MARIADB_GTID;
-    private $flag;
-    private $domainId;
-    private $mariaDbGtid;
+    private ConstEventsNames $type = ConstEventsNames::MARIADB_GTID;
 
     public function __construct(
         EventInfo $eventInfo,
-        int $flag,
-        int $domainId,
-        string $mariaDbGtid
+        public readonly int $flag,
+        public readonly int $domainId,
+        public readonly string $mariaDbGtid
     ) {
         parent::__construct($eventInfo);
-
-        $this->flag = $flag;
-        $this->domainId = $domainId;
-        $this->mariaDbGtid = $mariaDbGtid;
     }
 
     public function __toString(): string
@@ -31,37 +25,20 @@ public function __toString(): string
         return PHP_EOL .
             '=== Event ' . $this->getType() . ' === ' . PHP_EOL .
             'Date: ' . $this->eventInfo->getDateTime() . PHP_EOL .
-            'Log position: ' . $this->eventInfo->getPos() . PHP_EOL .
-            'Event size: ' . $this->eventInfo->getSize() . PHP_EOL .
+            'Log position: ' . $this->eventInfo->pos . PHP_EOL .
+            'Event size: ' . $this->eventInfo->size . PHP_EOL .
             'Flag: ' . var_export($this->flag, true) . PHP_EOL .
             'Domain Id: ' . $this->domainId . PHP_EOL .
             'Sequence Number: ' . $this->mariaDbGtid . PHP_EOL;
     }
 
-
     public function getType(): string
     {
-        return $this->type;
+        return $this->type->value;
     }
 
-
-    public function jsonSerialize()
+    public function jsonSerialize(): array
     {
         return get_object_vars($this);
     }
-
-    public function getFlag(): int
-    {
-        return $this->flag;
-    }
-
-    public function getMariaDbGtid(): string
-    {
-        return $this->mariaDbGtid;
-    }
-
-    public function getDomainId(): int
-    {
-        return $this->domainId;
-    }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/DTO/QueryDTO.php b/src/MySQLReplication/Event/DTO/QueryDTO.php
index 1ec3604d..1bc21ce8 100644
--- a/src/MySQLReplication/Event/DTO/QueryDTO.php
+++ b/src/MySQLReplication/Event/DTO/QueryDTO.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\DTO;
@@ -8,50 +9,16 @@
 
 class QueryDTO extends EventDTO
 {
-    private $executionTime;
-    private $query;
-    private $database;
-    private $type = ConstEventsNames::QUERY;
-    private $threadId;
+    private ConstEventsNames $type = ConstEventsNames::QUERY;
 
     public function __construct(
         EventInfo $eventInfo,
-        string $database,
-        int $executionTime,
-        string $query,
-        int $threadId
+        public readonly string $database,
+        public readonly int $executionTime,
+        public readonly string $query,
+        public readonly int $threadId
     ) {
         parent::__construct($eventInfo);
-
-        $this->executionTime = $executionTime;
-        $this->query = $query;
-        $this->database = $database;
-        $this->threadId = $threadId;
-    }
-
-    public function getDatabase(): string
-    {
-        return $this->database;
-    }
-
-    public function getExecutionTime(): int
-    {
-        return $this->executionTime;
-    }
-
-    public function getQuery(): string
-    {
-        return $this->query;
-    }
-
-    public function getType(): string
-    {
-        return $this->type;
-    }
-
-    public function getThreadId(): int
-    {
-        return $this->threadId;
     }
 
     public function __toString(): string
@@ -59,15 +26,21 @@ public function __toString(): string
         return PHP_EOL .
             '=== Event ' . $this->getType() . ' === ' . PHP_EOL .
             'Date: ' . $this->eventInfo->getDateTime() . PHP_EOL .
-            'Log position: ' . $this->eventInfo->getPos() . PHP_EOL .
-            'Event size: ' . $this->eventInfo->getSize() . PHP_EOL .
+            'Log position: ' . $this->eventInfo->pos . PHP_EOL .
+            'Event size: ' . $this->eventInfo->size . PHP_EOL .
             'Database: ' . $this->database . PHP_EOL .
             'Execution time: ' . $this->executionTime . PHP_EOL .
-            'Query: ' . $this->query . PHP_EOL;
+            'Query: ' . $this->query . PHP_EOL .
+            'Thread id: ' . $this->threadId . PHP_EOL;
+    }
+
+    public function getType(): string
+    {
+        return $this->type->value;
     }
 
-    public function jsonSerialize()
+    public function jsonSerialize(): array
     {
         return get_object_vars($this);
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/DTO/RotateDTO.php b/src/MySQLReplication/Event/DTO/RotateDTO.php
index d7bb6101..66ae072d 100644
--- a/src/MySQLReplication/Event/DTO/RotateDTO.php
+++ b/src/MySQLReplication/Event/DTO/RotateDTO.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\DTO;
@@ -8,34 +9,14 @@
 
 class RotateDTO extends EventDTO
 {
-    private $position;
-    private $nextBinlog;
-    private $type = ConstEventsNames::ROTATE;
+    private ConstEventsNames $type = ConstEventsNames::ROTATE;
 
     public function __construct(
         EventInfo $eventInfo,
-        int $position,
-        string $nextBinlog
+        public readonly string $position,
+        public readonly string $nextBinlog
     ) {
         parent::__construct($eventInfo);
-
-        $this->position = $position;
-        $this->nextBinlog = $nextBinlog;
-    }
-
-    public function getPosition(): int
-    {
-        return $this->position;
-    }
-
-    public function getNextBinlog(): string
-    {
-        return $this->nextBinlog;
-    }
-
-    public function getType(): string
-    {
-        return $this->type;
     }
 
     public function __toString(): string
@@ -43,14 +24,19 @@ public function __toString(): string
         return PHP_EOL .
             '=== Event ' . $this->getType() . ' === ' . PHP_EOL .
             'Date: ' . $this->eventInfo->getDateTime() . PHP_EOL .
-            'Log position: ' . $this->eventInfo->getPos() . PHP_EOL .
-            'Event size: ' . $this->eventInfo->getSize() . PHP_EOL .
+            'Log position: ' . $this->eventInfo->pos . PHP_EOL .
+            'Event size: ' . $this->eventInfo->size . PHP_EOL .
             'Binlog position: ' . $this->position . PHP_EOL .
             'Binlog filename: ' . $this->nextBinlog . PHP_EOL;
     }
 
-    public function jsonSerialize()
+    public function getType(): string
+    {
+        return $this->type->value;
+    }
+
+    public function jsonSerialize(): array
     {
         return get_object_vars($this);
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/DTO/RowsDTO.php b/src/MySQLReplication/Event/DTO/RowsDTO.php
index da6a2737..2b44c322 100644
--- a/src/MySQLReplication/Event/DTO/RowsDTO.php
+++ b/src/MySQLReplication/Event/DTO/RowsDTO.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\DTO;
@@ -8,36 +9,13 @@
 
 abstract class RowsDTO extends EventDTO
 {
-    private $values;
-    private $changedRows;
-    private $tableMap;
-
     public function __construct(
         EventInfo $eventInfo,
-        TableMap $tableMap,
-        int $changedRows,
-        array $values
+        public readonly TableMap $tableMap,
+        public readonly int $changedRows,
+        public readonly array $values
     ) {
         parent::__construct($eventInfo);
-
-        $this->changedRows = $changedRows;
-        $this->values = $values;
-        $this->tableMap = $tableMap;
-    }
-
-    public function getTableMap(): TableMap
-    {
-        return $this->tableMap;
-    }
-
-    public function getChangedRows(): int
-    {
-        return $this->changedRows;
-    }
-
-    public function getValues(): array
-    {
-        return $this->values;
     }
 
     public function __toString(): string
@@ -45,16 +23,16 @@ public function __toString(): string
         return PHP_EOL .
             '=== Event ' . $this->getType() . ' === ' . PHP_EOL .
             'Date: ' . $this->eventInfo->getDateTime() . PHP_EOL .
-            'Log position: ' . $this->eventInfo->getPos() . PHP_EOL .
-            'Event size: ' . $this->eventInfo->getSize() . PHP_EOL .
-            'Table: ' . $this->tableMap->getTable() . PHP_EOL .
-            'Affected columns: ' . $this->tableMap->getColumnsAmount() . PHP_EOL .
+            'Log position: ' . $this->eventInfo->pos . PHP_EOL .
+            'Event size: ' . $this->eventInfo->size . PHP_EOL .
+            'Table: ' . $this->tableMap->table . PHP_EOL .
+            'Affected columns: ' . $this->tableMap->columnsAmount . PHP_EOL .
             'Changed rows: ' . $this->changedRows . PHP_EOL .
             'Values: ' . print_r($this->values, true) . PHP_EOL;
     }
 
-    public function jsonSerialize()
+    public function jsonSerialize(): array
     {
         return get_object_vars($this);
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/DTO/TableMapDTO.php b/src/MySQLReplication/Event/DTO/TableMapDTO.php
index 64d31d48..96265e53 100644
--- a/src/MySQLReplication/Event/DTO/TableMapDTO.php
+++ b/src/MySQLReplication/Event/DTO/TableMapDTO.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\DTO;
@@ -9,16 +10,13 @@
 
 class TableMapDTO extends EventDTO
 {
-    private $type = ConstEventsNames::TABLE_MAP;
-    private $tableMap;
+    private ConstEventsNames $type = ConstEventsNames::TABLE_MAP;
 
     public function __construct(
         EventInfo $eventInfo,
-        TableMap $tableMap
+        public readonly TableMap $tableMap
     ) {
         parent::__construct($eventInfo);
-
-        $this->tableMap = $tableMap;
     }
 
     public function __toString(): string
@@ -26,26 +24,21 @@ public function __toString(): string
         return PHP_EOL .
             '=== Event ' . $this->getType() . ' === ' . PHP_EOL .
             'Date: ' . $this->eventInfo->getDateTime() . PHP_EOL .
-            'Log position: ' . $this->eventInfo->getPos() . PHP_EOL .
-            'Event size: ' . $this->eventInfo->getSize() . PHP_EOL .
-            'Table: ' . $this->tableMap->getTable() . PHP_EOL .
-            'Database: ' . $this->tableMap->getDatabase() . PHP_EOL .
-            'Table Id: ' . $this->tableMap->getTableId() . PHP_EOL .
-            'Columns amount: ' . $this->tableMap->getColumnsAmount() . PHP_EOL;
+            'Log position: ' . $this->eventInfo->pos . PHP_EOL .
+            'Event size: ' . $this->eventInfo->size . PHP_EOL .
+            'Table: ' . $this->tableMap->table . PHP_EOL .
+            'Database: ' . $this->tableMap->database . PHP_EOL .
+            'Table Id: ' . $this->tableMap->tableId . PHP_EOL .
+            'Columns amount: ' . $this->tableMap->columnsAmount . PHP_EOL;
     }
 
     public function getType(): string
     {
-        return $this->type;
+        return $this->type->value;
     }
 
-    public function jsonSerialize()
+    public function jsonSerialize(): array
     {
         return get_object_vars($this);
     }
-
-    public function getTableMap(): TableMap
-    {
-        return $this->tableMap;
-    }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/DTO/UpdateRowsDTO.php b/src/MySQLReplication/Event/DTO/UpdateRowsDTO.php
index aa59a599..ffe5a726 100644
--- a/src/MySQLReplication/Event/DTO/UpdateRowsDTO.php
+++ b/src/MySQLReplication/Event/DTO/UpdateRowsDTO.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\DTO;
@@ -7,9 +8,10 @@
 
 class UpdateRowsDTO extends RowsDTO
 {
-    protected $type = ConstEventsNames::UPDATE;
+    protected ConstEventsNames $type = ConstEventsNames::UPDATE;
+
     public function getType(): string
     {
-        return $this->type;
+        return $this->type->value;
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/DTO/WriteRowsDTO.php b/src/MySQLReplication/Event/DTO/WriteRowsDTO.php
index caa705e7..e46b5392 100644
--- a/src/MySQLReplication/Event/DTO/WriteRowsDTO.php
+++ b/src/MySQLReplication/Event/DTO/WriteRowsDTO.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\DTO;
@@ -7,10 +8,10 @@
 
 class WriteRowsDTO extends RowsDTO
 {
-    protected $type = ConstEventsNames::WRITE;
+    protected ConstEventsNames $type = ConstEventsNames::WRITE;
 
     public function getType(): string
     {
-        return $this->type;
+        return $this->type->value;
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/DTO/XidDTO.php b/src/MySQLReplication/Event/DTO/XidDTO.php
index ca729151..4150fbc8 100644
--- a/src/MySQLReplication/Event/DTO/XidDTO.php
+++ b/src/MySQLReplication/Event/DTO/XidDTO.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\DTO;
@@ -8,21 +9,13 @@
 
 class XidDTO extends EventDTO
 {
-    private $type = ConstEventsNames::XID;
-    private $xid;
+    private ConstEventsNames $type = ConstEventsNames::XID;
 
     public function __construct(
         EventInfo $eventInfo,
-        string $xid
+        public readonly string $xid
     ) {
         parent::__construct($eventInfo);
-
-        $this->xid = $xid;
-    }
-
-    public function getXid(): string
-    {
-        return $this->xid;
     }
 
     public function __toString(): string
@@ -30,18 +23,18 @@ public function __toString(): string
         return PHP_EOL .
             '=== Event ' . $this->getType() . ' === ' . PHP_EOL .
             'Date: ' . $this->eventInfo->getDateTime() . PHP_EOL .
-            'Log position: ' . $this->eventInfo->getPos() . PHP_EOL .
-            'Event size: ' . $this->eventInfo->getSize() . PHP_EOL .
+            'Log position: ' . $this->eventInfo->pos . PHP_EOL .
+            'Event size: ' . $this->eventInfo->size . PHP_EOL .
             'Transaction ID: ' . $this->xid . PHP_EOL;
     }
 
     public function getType(): string
     {
-        return $this->type;
+        return $this->type->value;
     }
 
-    public function jsonSerialize()
+    public function jsonSerialize(): array
     {
         return get_object_vars($this);
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/Event.php b/src/MySQLReplication/Event/Event.php
index 8a8066e0..4af0caf2 100644
--- a/src/MySQLReplication/Event/Event.php
+++ b/src/MySQLReplication/Event/Event.php
@@ -1,11 +1,10 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event;
 
 use MySQLReplication\BinaryDataReader\BinaryDataReader;
-use MySQLReplication\BinaryDataReader\BinaryDataReaderException;
-use MySQLReplication\BinLog\BinLogException;
 use MySQLReplication\BinLog\BinLogServerInfo;
 use MySQLReplication\BinLog\BinLogSocketConnect;
 use MySQLReplication\Config\Config;
@@ -15,92 +14,117 @@
 use MySQLReplication\Event\DTO\HeartbeatDTO;
 use MySQLReplication\Event\DTO\QueryDTO;
 use MySQLReplication\Event\RowEvent\RowEventFactory;
-use MySQLReplication\Exception\MySQLReplicationException;
-use MySQLReplication\JsonBinaryDecoder\JsonBinaryDecoderException;
-use MySQLReplication\Socket\SocketException;
 use Psr\SimpleCache\CacheInterface;
-use Psr\SimpleCache\InvalidArgumentException;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
-class Event
+readonly class Event
 {
     private const MARIADB_DUMMY_QUERY = '# Dum';
-    private const EOF_HEADER_VALUE = 254;
 
-    private $binLogSocketConnect;
-    private $rowEventFactory;
-    private $eventDispatcher;
-    private $cache;
+    private const EOF_HEADER_VALUE = 254;
 
     public function __construct(
-        BinLogSocketConnect $binLogSocketConnect,
-        RowEventFactory $rowEventFactory,
-        EventDispatcherInterface $eventDispatcher,
-        CacheInterface $cache
+        private BinLogSocketConnect $binLogSocketConnect,
+        private RowEventFactory $rowEventFactory,
+        private EventDispatcherInterface $eventDispatcher,
+        private CacheInterface $cache,
+        private Config $config,
+        private BinLogServerInfo $binLogServerInfo
     ) {
-        $this->binLogSocketConnect = $binLogSocketConnect;
-        $this->rowEventFactory = $rowEventFactory;
-        $this->eventDispatcher = $eventDispatcher;
-        $this->cache = $cache;
     }
 
-    /**
-     * @throws BinaryDataReaderException
-     * @throws BinLogException
-     * @throws MySQLReplicationException
-     * @throws JsonBinaryDecoderException
-     * @throws InvalidArgumentException
-     * @throws SocketException
-     */
     public function consume(): void
     {
         $binaryDataReader = new BinaryDataReader($this->binLogSocketConnect->getResponse());
 
         // check EOF_Packet -> https://dev.mysql.com/doc/internals/en/packet-EOF_Packet.html
-        if (self::EOF_HEADER_VALUE === $binaryDataReader->readUInt8()) {
+        if ($binaryDataReader->readUInt8() === self::EOF_HEADER_VALUE) {
             return;
         }
 
+        $this->dispatch($this->makeEvent($binaryDataReader));
+    }
+
+    private function makeEvent(BinaryDataReader $binaryDataReader): ?EventDTO
+    {
         // decode all events data
         $eventInfo = $this->createEventInfo($binaryDataReader);
 
-        $eventDTO = null;
-
-        // we always need this events to clean table maps and for BinLogCurrent class to keep track of binlog position
+        // we always need these events to clean table maps and for BinLogCurrent class to keep track of binlog position
         // always parse table map event but propagate when needed (we need this for creating table cache)
-        if (ConstEventType::TABLE_MAP_EVENT === $eventInfo->getType()) {
-            $eventDTO = $this->rowEventFactory->makeRowEvent($binaryDataReader, $eventInfo)->makeTableMapDTO();
-        } else if (ConstEventType::ROTATE_EVENT === $eventInfo->getType()) {
+        if ($eventInfo->type === ConstEventType::TABLE_MAP_EVENT->value) {
+            return $this->rowEventFactory->makeRowEvent($binaryDataReader, $eventInfo)
+                ->makeTableMapDTO();
+        }
+
+        if ($eventInfo->type === ConstEventType::ROTATE_EVENT->value) {
             $this->cache->clear();
-            $eventDTO = (new RotateEvent($eventInfo, $binaryDataReader))->makeRotateEventDTO();
-        } else if (ConstEventType::GTID_LOG_EVENT === $eventInfo->getType()) {
-            $eventDTO = (new GtidEvent($eventInfo, $binaryDataReader))->makeGTIDLogDTO();
-        } else if (ConstEventType::HEARTBEAT_LOG_EVENT === $eventInfo->getType()) {
-            $eventDTO = new HeartbeatDTO($eventInfo);
-        } else if (ConstEventType::MARIA_GTID_EVENT === $eventInfo->getType()) {
-            $eventDTO = (new MariaDbGtidEvent($eventInfo, $binaryDataReader))->makeMariaDbGTIDLogDTO();
+            return (new RotateEvent($eventInfo, $binaryDataReader, $this->binLogServerInfo))->makeRotateEventDTO();
+        }
+
+        if ($eventInfo->type === ConstEventType::GTID_LOG_EVENT->value) {
+            return (new GtidEvent($eventInfo, $binaryDataReader, $this->binLogServerInfo))->makeGTIDLogDTO();
+        }
+
+        if ($eventInfo->type === ConstEventType::HEARTBEAT_LOG_EVENT->value) {
+            return new HeartbeatDTO($eventInfo);
+        }
+
+        if ($eventInfo->type === ConstEventType::MARIA_GTID_EVENT->value) {
+            return (new MariaDbGtidEvent(
+                $eventInfo,
+                $binaryDataReader,
+                $this->binLogServerInfo
+            ))->makeMariaDbGTIDLogDTO();
         }
 
         // check for ignore and permitted events
-        if (!Config::checkEvent($eventInfo->getType())) {
-            return;
+        if ($this->ignoreEvent($eventInfo->type)) {
+            return null;
+        }
+
+        if (in_array(
+            $eventInfo->type,
+            [ConstEventType::UPDATE_ROWS_EVENT_V1->value, ConstEventType::UPDATE_ROWS_EVENT_V2->value],
+            true
+        )) {
+            return $this->rowEventFactory->makeRowEvent($binaryDataReader, $eventInfo)
+                ->makeUpdateRowsDTO();
+        }
+
+        if (in_array(
+            $eventInfo->type,
+            [ConstEventType::WRITE_ROWS_EVENT_V1->value, ConstEventType::WRITE_ROWS_EVENT_V2->value],
+            true
+        )) {
+            return $this->rowEventFactory->makeRowEvent($binaryDataReader, $eventInfo)
+                ->makeWriteRowsDTO();
+        }
+
+        if (in_array(
+            $eventInfo->type,
+            [ConstEventType::DELETE_ROWS_EVENT_V1->value, ConstEventType::DELETE_ROWS_EVENT_V2->value],
+            true
+        )) {
+            return $this->rowEventFactory->makeRowEvent($binaryDataReader, $eventInfo)
+                ->makeDeleteRowsDTO();
         }
 
-        if (in_array($eventInfo->getType(), [ConstEventType::UPDATE_ROWS_EVENT_V1, ConstEventType::UPDATE_ROWS_EVENT_V2], true)) {
-            $eventDTO = $this->rowEventFactory->makeRowEvent($binaryDataReader, $eventInfo)->makeUpdateRowsDTO();
-        } else if (in_array($eventInfo->getType(), [ConstEventType::WRITE_ROWS_EVENT_V1, ConstEventType::WRITE_ROWS_EVENT_V2], true)) {
-            $eventDTO = $this->rowEventFactory->makeRowEvent($binaryDataReader, $eventInfo)->makeWriteRowsDTO();
-        } else if (in_array($eventInfo->getType(), [ConstEventType::DELETE_ROWS_EVENT_V1, ConstEventType::DELETE_ROWS_EVENT_V2], true)) {
-            $eventDTO = $this->rowEventFactory->makeRowEvent($binaryDataReader, $eventInfo)->makeDeleteRowsDTO();
-        } else if (ConstEventType::XID_EVENT === $eventInfo->getType()) {
-            $eventDTO = (new XidEvent($eventInfo, $binaryDataReader))->makeXidDTO();
-        } else if (ConstEventType::QUERY_EVENT === $eventInfo->getType()) {
-            $eventDTO = $this->filterDummyMariaDbEvents((new QueryEvent($eventInfo, $binaryDataReader))->makeQueryDTO());
-        } else if (ConstEventType::FORMAT_DESCRIPTION_EVENT === $eventInfo->getType()) {
-            $eventDTO = new FormatDescriptionEventDTO($eventInfo);
+        if ($eventInfo->type === ConstEventType::XID_EVENT->value) {
+            return (new XidEvent($eventInfo, $binaryDataReader, $this->binLogServerInfo))->makeXidDTO();
         }
 
-        $this->dispatch($eventDTO);
+        if ($eventInfo->type === ConstEventType::QUERY_EVENT->value) {
+            return $this->filterDummyMariaDbEvents(
+                (new QueryEvent($eventInfo, $binaryDataReader, $this->binLogServerInfo))->makeQueryDTO()
+            );
+        }
+
+        if ($eventInfo->type === ConstEventType::FORMAT_DESCRIPTION_EVENT->value) {
+            return new FormatDescriptionEventDTO($eventInfo);
+        }
+
+        return null;
     }
 
     private function createEventInfo(BinaryDataReader $binaryDataReader): EventInfo
@@ -110,7 +134,7 @@ private function createEventInfo(BinaryDataReader $binaryDataReader): EventInfo
             $binaryDataReader->readUInt8(),
             $binaryDataReader->readInt32(),
             $binaryDataReader->readInt32(),
-            $binaryDataReader->readInt32(),
+            (string)$binaryDataReader->readInt32(),
             $binaryDataReader->readUInt16(),
             $this->binLogSocketConnect->getCheckSum(),
             $this->binLogSocketConnect->getBinLogCurrent()
@@ -119,17 +143,25 @@ private function createEventInfo(BinaryDataReader $binaryDataReader): EventInfo
 
     private function filterDummyMariaDbEvents(QueryDTO $queryDTO): ?QueryDTO
     {
-        if (BinLogServerInfo::isMariaDb() && false !== strpos($queryDTO->getQuery(), self::MARIADB_DUMMY_QUERY)) {
+        if ($this->binLogServerInfo->isMariaDb() && str_contains($queryDTO->query, self::MARIADB_DUMMY_QUERY)) {
             return null;
         }
 
         return $queryDTO;
     }
 
-    private function dispatch(EventDTO $eventDTO = null): void
+    private function dispatch(?EventDTO $eventDTO): void
     {
-        if (null !== $eventDTO) {
+        if ($eventDTO) {
+            if ($this->ignoreEvent($eventDTO->getEventInfo()->type)) {
+                return;
+            }
             $this->eventDispatcher->dispatch($eventDTO, $eventDTO->getType());
         }
     }
+
+    private function ignoreEvent(int $type): bool
+    {
+        return !$this->config->checkEvent($type);
+    }
 }
diff --git a/src/MySQLReplication/Event/EventCommon.php b/src/MySQLReplication/Event/EventCommon.php
index 8c40fa1b..e8426162 100644
--- a/src/MySQLReplication/Event/EventCommon.php
+++ b/src/MySQLReplication/Event/EventCommon.php
@@ -1,20 +1,18 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event;
 
 use MySQLReplication\BinaryDataReader\BinaryDataReader;
+use MySQLReplication\BinLog\BinLogServerInfo;
 
 abstract class EventCommon
 {
-    protected $eventInfo;
-    protected $binaryDataReader;
-
     public function __construct(
-        EventInfo $eventInfo,
-        BinaryDataReader $binaryDataReader
+        protected EventInfo $eventInfo,
+        protected BinaryDataReader $binaryDataReader,
+        protected BinLogServerInfo $binLogServerInfo
     ) {
-        $this->eventInfo = $eventInfo;
-        $this->binaryDataReader = $binaryDataReader;
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/EventInfo.php b/src/MySQLReplication/Event/EventInfo.php
index 054fc1d9..b11a2a27 100644
--- a/src/MySQLReplication/Event/EventInfo.php
+++ b/src/MySQLReplication/Event/EventInfo.php
@@ -1,55 +1,47 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event;
 
 use JsonSerializable;
 use MySQLReplication\BinLog\BinLogCurrent;
+use MySQLReplication\Definitions\ConstEventType;
 
 class EventInfo implements JsonSerializable
 {
-    private $timestamp;
-    private $type;
-    private $id;
-    private $size;
-    private $pos;
-    private $flag;
-    private $checkSum;
-    private $sizeNoHeader;
-    private $dateTime;
-    private $binLogCurrent;
+    private ?int $sizeNoHeader;
+    private ?string $dateTime;
+    private ?string $typeName;
 
     public function __construct(
-        int $timestamp,
-        int $type,
-        int $id,
-        int $size,
-        int $pos,
-        int $flag,
-        bool $checkSum,
-        BinLogCurrent $binLogCurrent
+        public readonly int $timestamp,
+        public readonly int $type,
+        public readonly int $serverId,
+        public readonly int $size,
+        public readonly string $pos,
+        public readonly int $flag,
+        public readonly bool $checkSum,
+        public readonly BinLogCurrent $binLogCurrent
     ) {
-        $this->timestamp = $timestamp;
-        $this->type = $type;
-        $this->id = $id;
-        $this->size = $size;
-        $this->pos = $pos;
-        $this->flag = $flag;
-        $this->checkSum = $checkSum;
-        $this->binLogCurrent = $binLogCurrent;
-
         if ($pos > 0) {
             $this->binLogCurrent->setBinLogPosition($pos);
         }
+        $this->sizeNoHeader = $this->dateTime = null;
+        $this->typeName = ConstEventType::from($this->type)->name;
     }
 
-    public function getBinLogCurrent(): BinLogCurrent
+    public function getTypeName(): ?string
     {
-        return $this->binLogCurrent;
+        return $this->typeName;
     }
 
-    public function getDateTime(): string
+    public function getDateTime(): ?string
     {
+        if ($this->timestamp === 0) {
+            return null;
+        }
+
         if (empty($this->dateTime)) {
             $this->dateTime = date('c', $this->timestamp);
         }
@@ -60,45 +52,14 @@ public function getDateTime(): string
     public function getSizeNoHeader(): int
     {
         if (empty($this->sizeNoHeader)) {
-            $this->sizeNoHeader = (true === $this->checkSum ? $this->size - 23 : $this->size - 19);
+            $this->sizeNoHeader = ($this->checkSum === true ? $this->size - 23 : $this->size - 19);
         }
 
         return $this->sizeNoHeader;
     }
 
-    public function getTimestamp(): int
-    {
-        return $this->timestamp;
-    }
-
-    public function getType(): int
-    {
-        return $this->type;
-    }
-
-    public function getId(): int
-    {
-        return $this->id;
-    }
-
-
-    public function getSize(): int
-    {
-        return $this->size;
-    }
-
-    public function getPos(): int
-    {
-        return $this->pos;
-    }
-
-    public function getFlag(): int
-    {
-        return $this->flag;
-    }
-
-    public function jsonSerialize()
+    public function jsonSerialize(): array
     {
         return get_object_vars($this);
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/EventSubscribers.php b/src/MySQLReplication/Event/EventSubscribers.php
index 768d35d2..1bb30c27 100644
--- a/src/MySQLReplication/Event/EventSubscribers.php
+++ b/src/MySQLReplication/Event/EventSubscribers.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event;
@@ -23,17 +24,17 @@ class EventSubscribers implements EventSubscriberInterface
     public static function getSubscribedEvents(): array
     {
         return [
-            ConstEventsNames::TABLE_MAP => 'onTableMap',
-            ConstEventsNames::UPDATE => 'onUpdate',
-            ConstEventsNames::DELETE => 'onDelete',
-            ConstEventsNames::GTID => 'onGTID',
-            ConstEventsNames::QUERY => 'onQuery',
-            ConstEventsNames::ROTATE => 'onRotate',
-            ConstEventsNames::XID => 'onXID',
-            ConstEventsNames::WRITE => 'onWrite',
-            ConstEventsNames::MARIADB_GTID => 'onMariaDbGtid',
-            ConstEventsNames::FORMAT_DESCRIPTION => 'onFormatDescription',
-            ConstEventsNames::HEARTBEAT => 'onHeartbeat',
+            ConstEventsNames::TABLE_MAP->value => 'onTableMap',
+            ConstEventsNames::UPDATE->value => 'onUpdate',
+            ConstEventsNames::DELETE->value => 'onDelete',
+            ConstEventsNames::GTID->value => 'onGTID',
+            ConstEventsNames::QUERY->value => 'onQuery',
+            ConstEventsNames::ROTATE->value => 'onRotate',
+            ConstEventsNames::XID->value => 'onXID',
+            ConstEventsNames::WRITE->value => 'onWrite',
+            ConstEventsNames::MARIADB_GTID->value => 'onMariaDbGtid',
+            ConstEventsNames::FORMAT_DESCRIPTION->value => 'onFormatDescription',
+            ConstEventsNames::HEARTBEAT->value => 'onHeartbeat',
         ];
     }
 
@@ -42,10 +43,6 @@ public function onUpdate(UpdateRowsDTO $event): void
         $this->allEvents($event);
     }
 
-    protected function allEvents(EventDTO $event): void
-    {
-    }
-
     public function onTableMap(TableMapDTO $event): void
     {
         $this->allEvents($event);
@@ -95,4 +92,8 @@ public function onHeartbeat(HeartbeatDTO $event): void
     {
         $this->allEvents($event);
     }
-}
\ No newline at end of file
+
+    protected function allEvents(EventDTO $event): void
+    {
+    }
+}
diff --git a/src/MySQLReplication/Event/GtidEvent.php b/src/MySQLReplication/Event/GtidEvent.php
index a023d02a..ae3fbb1a 100644
--- a/src/MySQLReplication/Event/GtidEvent.php
+++ b/src/MySQLReplication/Event/GtidEvent.php
@@ -1,26 +1,28 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event;
 
+use MySQLReplication\BinaryDataReader\BinaryDataReader;
 use MySQLReplication\Event\DTO\GTIDLogDTO;
 
 class GtidEvent extends EventCommon
 {
     public function makeGTIDLogDTO(): GTIDLogDTO
     {
-        $commit_flag = 1 === $this->binaryDataReader->readUInt8();
-        $sid = unpack('H*', $this->binaryDataReader->read(16))[1];
+        $commit_flag = $this->binaryDataReader->readUInt8() === 1;
+        $sid = BinaryDataReader::unpack('H*', $this->binaryDataReader->read(16))[1];
         $gno = $this->binaryDataReader->readUInt64();
 
-        $gtid = vsprintf('%s%s%s%s%s%s%s%s-%s%s%s%s-%s%s%s%s-%s%s%s%s-%s%s%s%s%s%s%s%s%s%s%s%s', str_split($sid)) . ':' . $gno;
+        $gtid = vsprintf(
+            '%s%s%s%s%s%s%s%s-%s%s%s%s-%s%s%s%s-%s%s%s%s-%s%s%s%s%s%s%s%s%s%s%s%s',
+            str_split($sid)
+        ) . ':' . $gno;
 
-        $this->eventInfo->getBinLogCurrent()->setGtid($gtid);
+        $this->eventInfo->binLogCurrent
+            ->setGtid($gtid);
 
-        return new GTIDLogDTO(
-            $this->eventInfo,
-            $commit_flag,
-            $gtid
-        );
+        return new GTIDLogDTO($this->eventInfo, $commit_flag, $gtid);
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/MariaDbGtidEvent.php b/src/MySQLReplication/Event/MariaDbGtidEvent.php
index 3eb9ae91..ddb45122 100644
--- a/src/MySQLReplication/Event/MariaDbGtidEvent.php
+++ b/src/MySQLReplication/Event/MariaDbGtidEvent.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event;
@@ -13,13 +14,9 @@ public function makeMariaDbGTIDLogDTO(): MariaDbGtidLogDTO
         $domainId = $this->binaryDataReader->readUInt32();
         $flag = $this->binaryDataReader->readUInt8();
 
-        $this->eventInfo->getBinLogCurrent()->setMariaDbGtid($mariaDbGtid);
+        $this->eventInfo->binLogCurrent
+            ->setMariaDbGtid($mariaDbGtid);
 
-        return new MariaDbGtidLogDTO(
-            $this->eventInfo,
-            $flag,
-            $domainId,
-            $mariaDbGtid
-        );
+        return new MariaDbGtidLogDTO($this->eventInfo, $flag, $domainId, $mariaDbGtid);
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/QueryEvent.php b/src/MySQLReplication/Event/QueryEvent.php
index 88f6b318..e20675f9 100644
--- a/src/MySQLReplication/Event/QueryEvent.php
+++ b/src/MySQLReplication/Event/QueryEvent.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event;
@@ -20,14 +21,10 @@ public function makeQueryDTO(): QueryDTO
         $this->binaryDataReader->advance($statusVarsLength);
         $schema = $this->binaryDataReader->read($schemaLength);
         $this->binaryDataReader->advance(1);
-        $query = $this->binaryDataReader->read($this->eventInfo->getSizeNoHeader() - 13 - $statusVarsLength - $schemaLength - 1);
-
-        return new QueryDTO(
-            $this->eventInfo,
-            $schema,
-            $executionTime,
-            $query,
-            $threadId
+        $query = $this->binaryDataReader->read(
+            $this->eventInfo->getSizeNoHeader() - 13 - $statusVarsLength - $schemaLength - 1
         );
+
+        return new QueryDTO($this->eventInfo, $schema, $executionTime, $query, $threadId);
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/RotateEvent.php b/src/MySQLReplication/Event/RotateEvent.php
index 0d68d863..8beac312 100644
--- a/src/MySQLReplication/Event/RotateEvent.php
+++ b/src/MySQLReplication/Event/RotateEvent.php
@@ -1,9 +1,9 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event;
 
-use MySQLReplication\BinLog\BinLogServerInfo;
 use MySQLReplication\Event\DTO\RotateDTO;
 
 /**
@@ -13,27 +13,25 @@ class RotateEvent extends EventCommon
 {
     public function makeRotateEventDTO(): RotateDTO
     {
-        $binFilePos = (int)$this->binaryDataReader->readUInt64();
+        $binFilePos = $this->binaryDataReader->readUInt64();
         $binFileName = $this->binaryDataReader->read(
             $this->eventInfo->getSizeNoHeader() - $this->getSizeToRemoveByVersion()
         );
 
-        $this->eventInfo->getBinLogCurrent()->setBinLogPosition($binFilePos);
-        $this->eventInfo->getBinLogCurrent()->setBinFileName($binFileName);
+        $this->eventInfo->binLogCurrent
+            ->setBinLogPosition($binFilePos);
+        $this->eventInfo->binLogCurrent
+            ->setBinFileName($binFileName);
 
-        return new RotateDTO(
-            $this->eventInfo,
-            $binFilePos,
-            $binFileName
-        );
+        return new RotateDTO($this->eventInfo, $binFilePos, $binFileName);
     }
 
     private function getSizeToRemoveByVersion(): int
     {
-        if (BinLogServerInfo::isMariaDb() && BinLogServerInfo::getRevision() <= 10) {
+        if ($this->binLogServerInfo->versionRevision <= 10 && $this->binLogServerInfo->isMariaDb()) {
             return 0;
         }
 
         return 8;
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/RowEvent/ColumnDTO.php b/src/MySQLReplication/Event/RowEvent/ColumnDTO.php
index 6767c72d..a0cad6d5 100644
--- a/src/MySQLReplication/Event/RowEvent/ColumnDTO.php
+++ b/src/MySQLReplication/Event/RowEvent/ColumnDTO.php
@@ -4,52 +4,29 @@
 
 namespace MySQLReplication\Event\RowEvent;
 
+use JsonSerializable;
 use MySQLReplication\BinaryDataReader\BinaryDataReader;
 use MySQLReplication\Definitions\ConstFieldType;
 use MySQLReplication\Repository\FieldDTO;
 
-class ColumnDTO
+readonly class ColumnDTO implements JsonSerializable
 {
-    private $fieldDTO;
-    private $maxLength;
-    private $size;
-    private $fsp;
-    private $lengthSize;
-    private $precision;
-    private $decimals;
-    private $bits;
-    private $bytes;
-    private $type;
-
     public function __construct(
-        FieldDTO $fieldDTO,
-        int $type,
-        int $maxLength,
-        int $size,
-        int $fsp,
-        int $lengthSize,
-        int $precision,
-        int $decimals,
-        int $bits,
-        int $bytes
+        public FieldDTO $fieldDTO,
+        public int $type,
+        public int $maxLength,
+        public int $size,
+        public int $fsp,
+        public int $lengthSize,
+        public int $precision,
+        public int $decimals,
+        public int $bits,
+        public int $bytes
     ) {
-        $this->fieldDTO = $fieldDTO;
-        $this->type = $type;
-        $this->maxLength = $maxLength;
-        $this->size = $size;
-        $this->fsp = $fsp;
-        $this->lengthSize = $lengthSize;
-        $this->precision = $precision;
-        $this->decimals = $decimals;
-        $this->bits = $bits;
-        $this->bytes = $bytes;
     }
 
-    public static function make(
-        int $columnType,
-        FieldDTO $fieldDTO,
-        BinaryDataReader $binaryDataReader
-    ): self {
+    public static function make(int $columnType, FieldDTO $fieldDTO, BinaryDataReader $binaryDataReader): self
+    {
         $maxLength = 0;
         $size = 0;
         $fsp = 0;
@@ -111,65 +88,15 @@ public static function make(
         );
     }
 
-    public function getFieldDTO(): FieldDTO
-    {
-        return $this->fieldDTO;
-    }
-
-    public function getMaxLength(): int
-    {
-        return $this->maxLength;
-    }
-
-    public function getSize(): int
-    {
-        return $this->size;
-    }
-
-    public function getFsp(): int
-    {
-        return $this->fsp;
-    }
-
-    public function getLengthSize(): int
-    {
-        return $this->lengthSize;
-    }
-
-    public function getPrecision(): int
-    {
-        return $this->precision;
-    }
-
-    public function getDecimals(): int
-    {
-        return $this->decimals;
-    }
-
-    public function getBits(): int
-    {
-        return $this->bits;
-    }
-
-    public function getBytes(): int
-    {
-        return $this->bytes;
-    }
-
-    public function getType(): int
-    {
-        return $this->type;
-    }
-
     public function getName(): string
     {
-        return $this->fieldDTO->getColumnName();
+        return $this->fieldDTO->columnName;
     }
 
     public function getEnumValues(): array
     {
         if ($this->type === ConstFieldType::ENUM) {
-            return explode(',', str_replace(['enum(', ')', '\''], '', $this->fieldDTO->getColumnType()));
+            return explode(',', str_replace(['enum(', ')', '\''], '', $this->fieldDTO->columnType));
         }
 
         return [];
@@ -178,7 +105,7 @@ public function getEnumValues(): array
     public function getSetValues(): array
     {
         if ($this->type === ConstFieldType::SET) {
-            return explode(',', str_replace(['set(', ')', '\''], '', $this->fieldDTO->getColumnType()));
+            return explode(',', str_replace(['set(', ')', '\''], '', $this->fieldDTO->columnType));
         }
 
         return [];
@@ -186,11 +113,16 @@ public function getSetValues(): array
 
     public function isUnsigned(): bool
     {
-        return !(stripos($this->fieldDTO->getColumnType(), 'unsigned') === false);
+        return !(stripos($this->fieldDTO->columnType, 'unsigned') === false);
     }
 
     public function isPrimary(): bool
     {
-        return $this->fieldDTO->getColumnKey() === 'PRI';
+        return $this->fieldDTO->columnKey === 'PRI';
+    }
+
+    public function jsonSerialize(): array
+    {
+        return get_object_vars($this);
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/RowEvent/ColumnDTOCollection.php b/src/MySQLReplication/Event/RowEvent/ColumnDTOCollection.php
index d5373187..2ea25e3f 100644
--- a/src/MySQLReplication/Event/RowEvent/ColumnDTOCollection.php
+++ b/src/MySQLReplication/Event/RowEvent/ColumnDTOCollection.php
@@ -1,10 +1,19 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\RowEvent;
 
 use Doctrine\Common\Collections\ArrayCollection;
+use JsonSerializable;
 
-class ColumnDTOCollection extends ArrayCollection
+/**
+ * @extends ArrayCollection<int, ColumnDTO>
+ */
+class ColumnDTOCollection extends ArrayCollection implements JsonSerializable
 {
-}
\ No newline at end of file
+    public function jsonSerialize(): array
+    {
+        return $this->toArray();
+    }
+}
diff --git a/src/MySQLReplication/Event/RowEvent/RowEvent.php b/src/MySQLReplication/Event/RowEvent/RowEvent.php
index 729cf46f..66d04af1 100644
--- a/src/MySQLReplication/Event/RowEvent/RowEvent.php
+++ b/src/MySQLReplication/Event/RowEvent/RowEvent.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\RowEvent;
@@ -6,7 +7,7 @@
 use DateTime;
 use Exception;
 use MySQLReplication\BinaryDataReader\BinaryDataReader;
-use MySQLReplication\BinaryDataReader\BinaryDataReaderException;
+use MySQLReplication\BinLog\BinLogServerInfo;
 use MySQLReplication\Config\Config;
 use MySQLReplication\Definitions\ConstEventType;
 use MySQLReplication\Definitions\ConstFieldType;
@@ -17,17 +18,15 @@
 use MySQLReplication\Event\EventCommon;
 use MySQLReplication\Event\EventInfo;
 use MySQLReplication\Exception\MySQLReplicationException;
-use MySQLReplication\JsonBinaryDecoder\JsonBinaryDecoderException;
 use MySQLReplication\JsonBinaryDecoder\JsonBinaryDecoderService;
 use MySQLReplication\Repository\FieldDTO;
 use MySQLReplication\Repository\RepositoryInterface;
-use Psr\SimpleCache\CacheInterface;
-use Psr\SimpleCache\InvalidArgumentException;
+use Psr\Log\LoggerInterface;
 use RuntimeException;
 
 class RowEvent extends EventCommon
 {
-    private static $bitCountInByte = [
+    private static array $bitCountInByte = [
         0,
         1,
         1,
@@ -285,32 +284,25 @@ class RowEvent extends EventCommon
         7,
         8,
     ];
-    private $repository;
-    private $cache;
 
-    /**
-     * @var TableMap|null
-     */
-    private $currentTableMap;
+    private ?TableMap $currentTableMap;
 
     public function __construct(
-        RepositoryInterface $repository,
-        BinaryDataReader $binaryDataReader,
-        EventInfo $eventInfo,
-        CacheInterface $cache
+        private readonly RepositoryInterface $repository,
+        protected BinaryDataReader $binaryDataReader,
+        protected EventInfo $eventInfo,
+        private readonly TableMapCache $tableMapCache,
+        private readonly Config $config,
+        protected BinLogServerInfo $binLogServerInfo,
+        private readonly LoggerInterface $logger
     ) {
-        parent::__construct($eventInfo, $binaryDataReader);
-
-        $this->repository = $repository;
-        $this->cache = $cache;
+        parent::__construct($eventInfo, $binaryDataReader, $binLogServerInfo);
     }
 
     /**
-     * This describe the structure of a table.
+     * This describes the structure of a table.
      * It's send before a change append on a table.
-     * A end user of the lib should have no usage of this
-     * @throws BinaryDataReaderException
-     * @throws InvalidArgumentException
+     * An end user of the lib should have no usage of this
      */
     public function makeTableMapDTO(): ?TableMapDTO
     {
@@ -320,7 +312,7 @@ public function makeTableMapDTO(): ?TableMapDTO
         $data['schema_length'] = $this->binaryDataReader->readUInt8();
         $data['schema_name'] = $this->binaryDataReader->read($data['schema_length']);
 
-        if (Config::checkDataBasesOnly($data['schema_name'])) {
+        if ($this->config->checkDataBasesOnly($data['schema_name'])) {
             return null;
         }
 
@@ -328,7 +320,7 @@ public function makeTableMapDTO(): ?TableMapDTO
         $data['table_length'] = $this->binaryDataReader->readUInt8();
         $data['table_name'] = $this->binaryDataReader->read($data['table_length']);
 
-        if (Config::checkTablesOnly($data['table_name'])) {
+        if ($this->config->checkTablesOnly($data['table_name'])) {
             return null;
         }
 
@@ -336,8 +328,8 @@ public function makeTableMapDTO(): ?TableMapDTO
         $data['columns_amount'] = (int)$this->binaryDataReader->readCodedBinary();
         $data['column_types'] = $this->binaryDataReader->read($data['columns_amount']);
 
-        if ($this->cache->has($data['table_id'])) {
-            return new TableMapDTO($this->eventInfo, $this->cache->get($data['table_id']));
+        if ($this->tableMapCache->has($data['table_id'])) {
+            return new TableMapDTO($this->eventInfo, $this->tableMapCache->get($data['table_id']));
         }
 
         $this->binaryDataReader->readCodedBinary();
@@ -356,9 +348,8 @@ public function makeTableMapDTO(): ?TableMapDTO
                     $type = ConstFieldType::IGNORE;
                 }
 
-                /** @var FieldDTO $fieldDTO */
                 $fieldDTO = $fieldDTOCollection->offsetGet($offset);
-                if (null !== $fieldDTO) {
+                if ($fieldDTO) {
                     $columnDTOCollection->set($offset, ColumnDTO::make($type, $fieldDTO, $this->binaryDataReader));
                 }
             }
@@ -372,78 +363,94 @@ public function makeTableMapDTO(): ?TableMapDTO
             $columnDTOCollection
         );
 
-        $this->cache->set($data['table_id'], $tableMap);
+        $this->tableMapCache->set($data['table_id'], $tableMap);
 
         return new TableMapDTO($this->eventInfo, $tableMap);
     }
 
-    /**
-     * @throws BinaryDataReaderException
-     * @throws InvalidArgumentException
-     * @throws JsonBinaryDecoderException
-     * @throws MySQLReplicationException
-     */
     public function makeWriteRowsDTO(): ?WriteRowsDTO
     {
-        if (!$this->rowInit()) {
+        $this->currentTableMap = $this->findTableMap();
+        if ($this->currentTableMap === null) {
             return null;
         }
 
         $values = $this->getValues();
 
-        return new WriteRowsDTO(
-            $this->eventInfo,
-            $this->currentTableMap,
-            count($values),
-            $values
-        );
+        return new WriteRowsDTO($this->eventInfo, $this->currentTableMap, count($values), $values);
     }
 
-    /**
-     * @throws InvalidArgumentException
-     * @throws BinaryDataReaderException
-     */
-    protected function rowInit(): bool
+    public function makeDeleteRowsDTO(): ?DeleteRowsDTO
+    {
+        $this->currentTableMap = $this->findTableMap();
+        if ($this->currentTableMap === null) {
+            return null;
+        }
+
+        $values = $this->getValues();
+
+        return new DeleteRowsDTO($this->eventInfo, $this->currentTableMap, count($values), $values);
+    }
+
+    public function makeUpdateRowsDTO(): ?UpdateRowsDTO
+    {
+        $this->currentTableMap = $this->findTableMap();
+        if ($this->currentTableMap === null) {
+            return null;
+        }
+
+        $columnsBinarySize = $this->getColumnsBinarySize($this->currentTableMap->columnsAmount);
+        $beforeBinaryData = $this->binaryDataReader->read($columnsBinarySize);
+        $afterBinaryData = $this->binaryDataReader->read($columnsBinarySize);
+
+        $values = [];
+        while (!$this->binaryDataReader->isComplete($this->eventInfo->getSizeNoHeader())) {
+            $values[] = [
+                'before' => $this->getColumnData($beforeBinaryData),
+                'after' => $this->getColumnData($afterBinaryData),
+            ];
+        }
+
+        return new UpdateRowsDTO($this->eventInfo, $this->currentTableMap, count($values), $values);
+    }
+
+    protected function findTableMap(): ?TableMap
     {
         $tableId = $this->binaryDataReader->readTableId();
         $this->binaryDataReader->advance(2);
 
         if (in_array(
-            $this->eventInfo->getType(), [
-            ConstEventType::DELETE_ROWS_EVENT_V2,
-            ConstEventType::WRITE_ROWS_EVENT_V2,
-            ConstEventType::UPDATE_ROWS_EVENT_V2
-        ], true
+            $this->eventInfo->type,
+            [
+                ConstEventType::DELETE_ROWS_EVENT_V2->value,
+                ConstEventType::WRITE_ROWS_EVENT_V2->value,
+                ConstEventType::UPDATE_ROWS_EVENT_V2->value,
+            ],
+            true
         )) {
             $this->binaryDataReader->read((int)($this->binaryDataReader->readUInt16() / 8));
         }
 
         $this->binaryDataReader->readCodedBinary();
 
-        if ($this->cache->has($tableId)) {
-            /** @var TableMap $tableMap */
-            $this->currentTableMap = $this->cache->get($tableId);
-
-            return true;
+        if ($this->tableMapCache->has($tableId)) {
+            return $this->tableMapCache->get($tableId);
         }
 
-        return false;
+        $this->logger->info('No table map found for table ID: ' . $tableId);
+
+        return null;
     }
 
-    /**
-     * @throws BinaryDataReaderException
-     * @throws JsonBinaryDecoderException
-     * @throws MySQLReplicationException
-     */
     protected function getValues(): array
     {
         // if we don't get columns from information schema we don't know how to assign them
-        if ($this->currentTableMap === null || $this->currentTableMap->getColumnDTOCollection()->isEmpty()) {
+        if ($this->currentTableMap === null || $this->currentTableMap->columnDTOCollection->isEmpty()) {
             return [];
         }
 
         $binaryData = $this->binaryDataReader->read(
-            $this->getColumnsBinarySize($this->currentTableMap->getColumnsAmount())
+            $this->getColumnsBinarySize($this->currentTableMap->columnsAmount)
         );
 
         $values = [];
@@ -459,14 +466,9 @@ protected function getColumnsBinarySize(int $columnsAmount): int
         return (int)(($columnsAmount + 7) / 8);
     }
 
-    /**
-     * @throws BinaryDataReaderException
-     * @throws JsonBinaryDecoderException
-     * @throws MySQLReplicationException
-     */
     protected function getColumnData(string $colsBitmap): array
     {
-        if (null === $this->currentTableMap) {
+        if ($this->currentTableMap === null) {
             throw new RuntimeException('Current table map is missing!');
         }
 
@@ -477,89 +479,91 @@ protected function getColumnData(string $colsBitmap): array
         $nullBitmap = $this->binaryDataReader->read($this->getColumnsBinarySize($this->bitCount($colsBitmap)));
         $nullBitmapIndex = 0;
 
-        foreach ($this->currentTableMap->getColumnDTOCollection() as $i => $columnDTO) {
+        foreach ($this->currentTableMap->columnDTOCollection as $i => $columnDTO) {
             $name = $columnDTO->getName();
-            $type = $columnDTO->getType();
+            $type = $columnDTO->type;
 
-            if (0 === $this->bitGet($colsBitmap, $i)) {
+            if ($this->bitGet($colsBitmap, $i) === 0) {
                 $values[$name] = null;
                 continue;
             }
 
             if ($this->checkNull($nullBitmap, $nullBitmapIndex)) {
                 $values[$name] = null;
-            } else if ($type === ConstFieldType::IGNORE) {
-                $this->binaryDataReader->advance($columnDTO->getLengthSize());
+            } elseif ($type === ConstFieldType::IGNORE) {
+                $this->binaryDataReader->advance($columnDTO->lengthSize);
                 $values[$name] = null;
-            } else if ($type === ConstFieldType::TINY) {
+            } elseif ($type === ConstFieldType::TINY) {
                 if ($columnDTO->isUnsigned()) {
                     $values[$name] = $this->binaryDataReader->readUInt8();
                 } else {
                     $values[$name] = $this->binaryDataReader->readInt8();
                 }
-            } else if ($type === ConstFieldType::SHORT) {
+            } elseif ($type === ConstFieldType::SHORT) {
                 if ($columnDTO->isUnsigned()) {
                     $values[$name] = $this->binaryDataReader->readUInt16();
                 } else {
                     $values[$name] = $this->binaryDataReader->readInt16();
                 }
-            } else if ($type === ConstFieldType::LONG) {
+            } elseif ($type === ConstFieldType::LONG) {
                 if ($columnDTO->isUnsigned()) {
                     $values[$name] = $this->binaryDataReader->readUInt32();
                 } else {
                     $values[$name] = $this->binaryDataReader->readInt32();
                 }
-            } else if ($type === ConstFieldType::LONGLONG) {
+            } elseif ($type === ConstFieldType::LONGLONG) {
                 if ($columnDTO->isUnsigned()) {
                     $values[$name] = $this->binaryDataReader->readUInt64();
                 } else {
                     $values[$name] = $this->binaryDataReader->readInt64();
                 }
-            } else if ($type === ConstFieldType::INT24) {
+            } elseif ($type === ConstFieldType::INT24) {
                 if ($columnDTO->isUnsigned()) {
                     $values[$name] = $this->binaryDataReader->readUInt24();
                 } else {
                     $values[$name] = $this->binaryDataReader->readInt24();
                 }
-            } else if ($type === ConstFieldType::FLOAT) {
+            } elseif ($type === ConstFieldType::FLOAT) {
                 // http://dev.mysql.com/doc/refman/5.7/en/floating-point-types.html FLOAT(7,4)
                 $values[$name] = round($this->binaryDataReader->readFloat(), 4);
-            } else if ($type === ConstFieldType::DOUBLE) {
+            } elseif ($type === ConstFieldType::DOUBLE) {
                 $values[$name] = $this->binaryDataReader->readDouble();
-            } else if ($type === ConstFieldType::VARCHAR || $type === ConstFieldType::STRING) {
-                $values[$name] = $columnDTO->getMaxLength() > 255 ? $this->getString(2) : $this->getString(1);
-            } else if ($type === ConstFieldType::NEWDECIMAL) {
+            } elseif ($type === ConstFieldType::VARCHAR || $type === ConstFieldType::STRING) {
+                $values[$name] = $columnDTO->maxLength > 255 ? $this->getString(2) : $this->getString(1);
+            } elseif ($type === ConstFieldType::NEWDECIMAL) {
                 $values[$name] = $this->getDecimal($columnDTO);
-            } else if ($type === ConstFieldType::BLOB) {
-                $values[$name] = $this->getString($columnDTO->getLengthSize());
-            } else if ($type === ConstFieldType::DATETIME) {
+            } elseif ($type === ConstFieldType::BLOB) {
+                $values[$name] = $this->getString($columnDTO->lengthSize);
+            } elseif ($type === ConstFieldType::DATETIME) {
                 $values[$name] = $this->getDatetime();
-            } else if ($type === ConstFieldType::DATETIME2) {
+            } elseif ($type === ConstFieldType::DATETIME2) {
                 $values[$name] = $this->getDatetime2($columnDTO);
-            } else if ($type === ConstFieldType::TIMESTAMP) {
+            } elseif ($type === ConstFieldType::TIMESTAMP) {
                 $values[$name] = date('Y-m-d H:i:s', $this->binaryDataReader->readUInt32());
-            } else if ($type === ConstFieldType::TIME) {
+            } elseif ($type === ConstFieldType::TIME) {
                 $values[$name] = $this->getTime();
-            } else if ($type === ConstFieldType::TIME2) {
+            } elseif ($type === ConstFieldType::TIME2) {
                 $values[$name] = $this->getTime2($columnDTO);
-            } else if ($type === ConstFieldType::TIMESTAMP2) {
+            } elseif ($type === ConstFieldType::TIMESTAMP2) {
                 $values[$name] = $this->getTimestamp2($columnDTO);
-            } else if ($type === ConstFieldType::DATE) {
+            } elseif ($type === ConstFieldType::DATE) {
                 $values[$name] = $this->getDate();
-            } else if ($type === ConstFieldType::YEAR) {
+            } elseif ($type === ConstFieldType::YEAR) {
                 // https://dev.mysql.com/doc/refman/5.7/en/year.html
                 $year = $this->binaryDataReader->readUInt8();
-                $values[$name] = 0 === $year ? null : 1900 + $year;
-            } else if ($type === ConstFieldType::ENUM) {
+                $values[$name] = $year === 0 ? null : 1900 + $year;
+            } elseif ($type === ConstFieldType::ENUM) {
                 $values[$name] = $this->getEnum($columnDTO);
-            } else if ($type === ConstFieldType::SET) {
+            } elseif ($type === ConstFieldType::SET) {
                 $values[$name] = $this->getSet($columnDTO);
-            } else if ($type === ConstFieldType::BIT) {
+            } elseif ($type === ConstFieldType::BIT) {
                 $values[$name] = $this->getBit($columnDTO);
-            } else if ($type === ConstFieldType::GEOMETRY) {
-                $values[$name] = $this->getString($columnDTO->getLengthSize());
-            } else if ($type === ConstFieldType::JSON) {
-                $values[$name] = JsonBinaryDecoderService::makeJsonBinaryDecoder($this->getString($columnDTO->getLengthSize()))->parseToString();
+            } elseif ($type === ConstFieldType::GEOMETRY) {
+                $values[$name] = $this->getString($columnDTO->lengthSize);
+            } elseif ($type === ConstFieldType::JSON) {
+                $values[$name] = JsonBinaryDecoderService::makeJsonBinaryDecoder(
+                    $this->getString($columnDTO->lengthSize)
+                )->parseToString();
             } else {
                 throw new MySQLReplicationException('Unknown row type: ' . $type);
             }
@@ -605,9 +609,6 @@ protected function checkNull(string $nullBitmap, int $position): int
         return $this->getBitFromBitmap($nullBitmap, $position) & (1 << ($position % 8));
     }
 
-    /**
-     * @throws BinaryDataReaderException
-     */
     protected function getString(int $size): string
     {
         return $this->binaryDataReader->readLengthString($size);
@@ -616,17 +617,16 @@ protected function getString(int $size): string
     /**
      * Read MySQL's new decimal format introduced in MySQL 5
      * https://dev.mysql.com/doc/refman/5.6/en/precision-math-decimal-characteristics.html
-     * @throws BinaryDataReaderException
      */
     protected function getDecimal(ColumnDTO $columnDTO): string
     {
         $digitsPerInteger = 9;
         $compressedBytes = [0, 1, 1, 2, 2, 3, 3, 4, 4, 4];
-        $integral = $columnDTO->getPrecision() - $columnDTO->getDecimals();
+        $integral = $columnDTO->precision - $columnDTO->decimals;
         $unCompIntegral = (int)($integral / $digitsPerInteger);
-        $unCompFractional = (int)($columnDTO->getDecimals() / $digitsPerInteger);
+        $unCompFractional = (int)($columnDTO->decimals / $digitsPerInteger);
         $compIntegral = $integral - ($unCompIntegral * $digitsPerInteger);
-        $compFractional = $columnDTO->getDecimals() - ($unCompFractional * $digitsPerInteger);
+        $compFractional = $columnDTO->decimals - ($unCompFractional * $digitsPerInteger);
 
         $value = $this->binaryDataReader->readUInt8();
         if (0 !== ($value & 0x80)) {
@@ -662,23 +662,18 @@ protected function getDecimal(ColumnDTO $columnDTO): string
             $res .= sprintf('%0' . $compFractional . 'd', $value);
         }
 
-        return bcmul($res, '1', $columnDTO->getDecimals());
+        return bcmul($res, '1', $columnDTO->decimals);
     }
 
     protected function getDatetime(): ?string
     {
         $value = $this->binaryDataReader->readUInt64();
         // nasty mysql 0000-00-00 dates
-        if ('0' === $value) {
+        if ($value === '0') {
             return null;
         }
 
-        $date = DateTime::createFromFormat('YmdHis', $value)->format('Y-m-d H:i:s');
-        if (array_sum(DateTime::getLastErrors()) > 0) {
-            return null;
-        }
-
-        return $date;
+        return $this->dateFormatter($value);
     }
 
     /**
@@ -692,8 +687,6 @@ protected function getDatetime(): ?string
      * ---------------------------
      * 40 bits = 5 bytes
      *
-     * @throws BinaryDataReaderException
-     *
      * @link https://dev.mysql.com/doc/internals/en/date-and-time-data-type-representation.html
      */
     protected function getDatetime2(ColumnDTO $columnDTO): ?string
@@ -710,39 +703,54 @@ protected function getDatetime2(ColumnDTO $columnDTO): ?string
         $second = $this->binaryDataReader->getBinarySlice($data, 34, 6, 40);
         $fsp = $this->getFSP($columnDTO);
 
+        $formattedDate = $this->dateFormatter(
+            $year . '-' . $month . '-' . $day . ' ' . $hour . ':' . $minute . ':' . $second
+        );
+        if ($formattedDate) {
+            return $formattedDate . $fsp;
+        }
+
+        return null;
+    }
+
+    protected function dateFormatter(string $date): ?string
+    {
         try {
-            $date = new DateTime($year . '-' . $month . '-' . $day . ' ' . $hour . ':' . $minute . ':' . $second);
-        } catch (Exception $exception) {
-            return null;
+            $dateTime = new DateTime($date);
+        } catch (Exception) {
+            $dateTime = DateTime::createFromFormat('YmdHis', $date);
+            if ($dateTime === false) {
+                return null;
+            }
         }
-        if (array_sum(DateTime::getLastErrors()) > 0) {
+
+        $formattedDate = $dateTime->format('Y-m-d H:i:s');
+        if (DateTime::getLastErrors() !== false) {
             return null;
         }
 
-        return $date->format('Y-m-d H:i:s') . $fsp;
+        return $formattedDate;
     }
 
     /**
-     * @throws BinaryDataReaderException
      * @link https://dev.mysql.com/doc/internals/en/date-and-time-data-type-representation.html
      */
     protected function getFSP(ColumnDTO $columnDTO): string
     {
         $read = 0;
         $time = '';
-        $fsp = $columnDTO->getFsp();
+        $fsp = $columnDTO->fsp;
         if ($fsp === 1 || $fsp === 2) {
             $read = 1;
-        } else if ($fsp === 3 || $fsp === 4) {
+        } elseif ($fsp === 3 || $fsp === 4) {
             $read = 2;
-        } else if ($fsp === 5 || $fsp === 6) {
+        } elseif ($fsp === 5 || $fsp === 6) {
             $read = 3;
         }
         if ($read > 0) {
             $microsecond = $this->binaryDataReader->readIntBeBySize($read);
             if ($fsp % 2) {
                 $microsecond = (int)($microsecond / 10);
-
             }
             $time = $microsecond * (10 ** (6 - $fsp));
         }
@@ -753,7 +761,7 @@ protected function getFSP(ColumnDTO $columnDTO): string
     protected function getTime(): string
     {
         $data = $this->binaryDataReader->readUInt24();
-        if (0 === $data) {
+        if ($data === 0) {
             return '00:00:00';
         }
 
@@ -761,7 +769,7 @@ protected function getTime(): string
     }
 
     /**
-     * TIME encoding for non fractional part:
+     * TIME encoding for non-fractional part:
      * 1 bit sign    (1= non-negative, 0= negative)
      * 1 bit unused  (reserved for future extensions)
      * 10 bits hour   (0-838)
@@ -769,8 +777,6 @@ protected function getTime(): string
      * 6 bits second (0-59)
      * ---------------------
      * 24 bits = 3 bytes
-     *
-     * @throws BinaryDataReaderException
      */
     protected function getTime2(ColumnDTO $columnDTO): string
     {
@@ -780,17 +786,16 @@ protected function getTime2(ColumnDTO $columnDTO): string
         $minute = $this->binaryDataReader->getBinarySlice($data, 12, 6, 24);
         $second = $this->binaryDataReader->getBinarySlice($data, 18, 6, 24);
 
-        return (new DateTime())->setTime($hour, $minute, $second)->format('H:i:s') . $this->getFSP($columnDTO);
+        return (new DateTime())
+                ->setTime($hour, $minute, $second)
+                ->format('H:i:s') . $this->getFSP($columnDTO);
     }
 
-    /**
-     * @throws BinaryDataReaderException
-     */
     protected function getTimestamp2(ColumnDTO $columnDTO): string
     {
-        $datetime = (string)date('Y-m-d H:i:s', $this->binaryDataReader->readInt32Be());
+        $datetime = date('Y-m-d H:i:s', $this->binaryDataReader->readInt32Be());
         $fsp = $this->getFSP($columnDTO);
-        if ('' !== $fsp) {
+        if ($fsp !== '') {
             $datetime .= '.' . $fsp;
         }
 
@@ -800,7 +805,7 @@ protected function getTimestamp2(ColumnDTO $columnDTO): string
     protected function getDate(): ?string
     {
         $time = $this->binaryDataReader->readUInt24();
-        if (0 === $time) {
+        if ($time === 0) {
             return null;
         }
 
@@ -811,15 +816,13 @@ protected function getDate(): ?string
             return null;
         }
 
-        return (new DateTime())->setDate($year, $month, $day)->format('Y-m-d');
+        return (new DateTime())->setDate($year, $month, $day)
+            ->format('Y-m-d');
     }
 
-    /**
-     * @throws BinaryDataReaderException
-     */
     protected function getEnum(ColumnDTO $columnDTO): string
     {
-        $value = $this->binaryDataReader->readUIntBySize($columnDTO->getSize()) - 1;
+        $value = $this->binaryDataReader->readUIntBySize($columnDTO->size) - 1;
 
         // check if given value exists in enums, if there not existing enum mysql returns empty string.
         if (array_key_exists($value, $columnDTO->getEnumValues())) {
@@ -829,16 +832,13 @@ protected function getEnum(ColumnDTO $columnDTO): string
         return '';
     }
 
-    /**
-     * @throws BinaryDataReaderException
-     */
     protected function getSet(ColumnDTO $columnDTO): array
     {
         // we read set columns as a bitmap telling us which options are enabled
-        $bit_mask = $this->binaryDataReader->readUIntBySize($columnDTO->getSize());
+        $bitMask = $this->binaryDataReader->readUIntBySize($columnDTO->size);
         $sets = [];
         foreach ($columnDTO->getSetValues() as $k => $item) {
-            if ($bit_mask & (2 ** $k)) {
+            if ($bitMask & (2 ** $k)) {
                 $sets[] = $item;
             }
         }
@@ -849,15 +849,15 @@ protected function getSet(ColumnDTO $columnDTO): array
     protected function getBit(ColumnDTO $columnDTO): string
     {
         $res = '';
-        for ($byte = 0; $byte < $columnDTO->getBytes(); ++$byte) {
-            $current_byte = '';
+        for ($byte = 0; $byte < $columnDTO->bytes; ++$byte) {
+            $currentByte = '';
             $data = $this->binaryDataReader->readUInt8();
-            if (0 === $byte) {
-                if (1 === $columnDTO->getBytes()) {
-                    $end = $columnDTO->getBits();
+            if ($byte === 0) {
+                if ($columnDTO->bytes === 1) {
+                    $end = $columnDTO->bits;
                 } else {
-                    $end = $columnDTO->getBits() % 8;
-                    if (0 === $end) {
+                    $end = $columnDTO->bits % 8;
+                    if ($end === 0) {
                         $end = 8;
                     }
                 }
@@ -867,69 +867,14 @@ protected function getBit(ColumnDTO $columnDTO): string
 
             for ($bit = 0; $bit < $end; ++$bit) {
                 if ($data & (1 << $bit)) {
-                    $current_byte .= '1';
+                    $currentByte .= '1';
                 } else {
-                    $current_byte .= '0';
+                    $currentByte .= '0';
                 }
-
             }
-            $res .= strrev($current_byte);
+            $res .= strrev($currentByte);
         }
 
         return $res;
     }
-
-    /**
-     * @throws InvalidArgumentException
-     * @throws BinaryDataReaderException
-     * @throws JsonBinaryDecoderException
-     * @throws MySQLReplicationException
-     */
-    public function makeDeleteRowsDTO(): ?DeleteRowsDTO
-    {
-        if (!$this->rowInit()) {
-            return null;
-        }
-
-        $values = $this->getValues();
-
-        return new DeleteRowsDTO(
-            $this->eventInfo,
-            $this->currentTableMap,
-            count($values),
-            $values
-        );
-    }
-
-    /**
-     * @throws InvalidArgumentException
-     * @throws BinaryDataReaderException
-     * @throws JsonBinaryDecoderException
-     * @throws MySQLReplicationException
-     */
-    public function makeUpdateRowsDTO(): ?UpdateRowsDTO
-    {
-        if (!$this->rowInit()) {
-            return null;
-        }
-
-        $columnsBinarySize = $this->getColumnsBinarySize($this->currentTableMap->getColumnsAmount());
-        $beforeBinaryData = $this->binaryDataReader->read($columnsBinarySize);
-        $afterBinaryData = $this->binaryDataReader->read($columnsBinarySize);
-
-        $values = [];
-        while (!$this->binaryDataReader->isComplete($this->eventInfo->getSizeNoHeader())) {
-            $values[] = [
-                'before' => $this->getColumnData($beforeBinaryData),
-                'after' => $this->getColumnData($afterBinaryData)
-            ];
-        }
-
-        return new UpdateRowsDTO(
-            $this->eventInfo,
-            $this->currentTableMap,
-            count($values),
-            $values
-        );
-    }
 }
diff --git a/src/MySQLReplication/Event/RowEvent/RowEventBuilder.php b/src/MySQLReplication/Event/RowEvent/RowEventBuilder.php
index 2fc4eafe..cc904a7e 100644
--- a/src/MySQLReplication/Event/RowEvent/RowEventBuilder.php
+++ b/src/MySQLReplication/Event/RowEvent/RowEventBuilder.php
@@ -1,46 +1,46 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\RowEvent;
 
 use MySQLReplication\BinaryDataReader\BinaryDataReader;
+use MySQLReplication\BinLog\BinLogServerInfo;
+use MySQLReplication\Config\Config;
 use MySQLReplication\Event\EventInfo;
 use MySQLReplication\Repository\RepositoryInterface;
+use Psr\Log\LoggerInterface;
 use Psr\SimpleCache\CacheInterface;
 
 class RowEventBuilder
 {
-    private $repository;
-    private $cache;
-    /**
-     * @var BinaryDataReader
-     */
-    private $package;
-    /**
-     * @var EventInfo
-     */
-    private $eventInfo;
+    private BinaryDataReader $binaryDataReader;
+    private EventInfo $eventInfo;
 
     public function __construct(
-        RepositoryInterface $repository,
-        CacheInterface $cache
+        private readonly RepositoryInterface $repository,
+        private readonly CacheInterface $cache,
+        private readonly Config $config,
+        private readonly BinLogServerInfo $binLogServerInfo,
+        private readonly LoggerInterface $logger
     ) {
-        $this->repository = $repository;
-        $this->cache = $cache;
     }
 
-    public function withPackage(BinaryDataReader $package): void
+    public function withBinaryDataReader(BinaryDataReader $binaryDataReader): void
     {
-        $this->package = $package;
+        $this->binaryDataReader = $binaryDataReader;
     }
 
     public function build(): RowEvent
     {
         return new RowEvent(
             $this->repository,
-            $this->package,
+            $this->binaryDataReader,
             $this->eventInfo,
-            $this->cache
+            new TableMapCache($this->cache),
+            $this->config,
+            $this->binLogServerInfo,
+            $this->logger
         );
     }
 
@@ -48,4 +48,4 @@ public function withEventInfo(EventInfo $eventInfo): void
     {
         $this->eventInfo = $eventInfo;
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/RowEvent/RowEventFactory.php b/src/MySQLReplication/Event/RowEvent/RowEventFactory.php
index 06ef80b5..ca1b8c67 100644
--- a/src/MySQLReplication/Event/RowEvent/RowEventFactory.php
+++ b/src/MySQLReplication/Event/RowEvent/RowEventFactory.php
@@ -1,29 +1,24 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\RowEvent;
 
 use MySQLReplication\BinaryDataReader\BinaryDataReader;
 use MySQLReplication\Event\EventInfo;
-use MySQLReplication\Repository\RepositoryInterface;
-use Psr\SimpleCache\CacheInterface;
 
-class RowEventFactory
+readonly class RowEventFactory
 {
-    private $rowEventBuilder;
-
     public function __construct(
-        RepositoryInterface $repository,
-        CacheInterface $cache
+        private RowEventBuilder $rowEventBuilder
     ) {
-        $this->rowEventBuilder = new RowEventBuilder($repository, $cache);
     }
 
-    public function makeRowEvent(BinaryDataReader $package, EventInfo $eventInfo): RowEvent
+    public function makeRowEvent(BinaryDataReader $binaryDataReader, EventInfo $eventInfo): RowEvent
     {
-        $this->rowEventBuilder->withPackage($package);
+        $this->rowEventBuilder->withBinaryDataReader($binaryDataReader);
         $this->rowEventBuilder->withEventInfo($eventInfo);
 
         return $this->rowEventBuilder->build();
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/RowEvent/TableMap.php b/src/MySQLReplication/Event/RowEvent/TableMap.php
index 9b38e95e..5ceffadb 100644
--- a/src/MySQLReplication/Event/RowEvent/TableMap.php
+++ b/src/MySQLReplication/Event/RowEvent/TableMap.php
@@ -1,62 +1,24 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event\RowEvent;
 
 use JsonSerializable;
 
-class TableMap implements JsonSerializable
+readonly class TableMap implements JsonSerializable
 {
-    private $database;
-    private $table;
-    private $tableId;
-    private $columnsAmount;
-    private $columnDTOCollection;
-
     public function __construct(
-        string $database,
-        string $table,
-        string $tableId,
-        int $columnsAmount,
-        ColumnDTOCollection $columnDTOCollection
+        public string $database,
+        public string $table,
+        public string $tableId,
+        public int $columnsAmount,
+        public ColumnDTOCollection $columnDTOCollection
     ) {
-        $this->database = $database;
-        $this->table = $table;
-        $this->tableId = $tableId;
-        $this->columnsAmount = $columnsAmount;
-        $this->columnDTOCollection = $columnDTOCollection;
-    }
-
-    public function getDatabase(): string
-    {
-        return $this->database;
-    }
-
-    public function getTable(): string
-    {
-        return $this->table;
-    }
-
-    public function getTableId(): string
-    {
-        return $this->tableId;
-    }
-
-    public function getColumnsAmount(): int
-    {
-        return $this->columnsAmount;
-    }
-
-    /**
-     * @return ColumnDTOCollection|ColumnDTO[]
-     */
-    public function getColumnDTOCollection(): ColumnDTOCollection
-    {
-        return $this->columnDTOCollection;
     }
 
-    public function jsonSerialize()
+    public function jsonSerialize(): array
     {
         return get_object_vars($this);
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Event/RowEvent/TableMapCache.php b/src/MySQLReplication/Event/RowEvent/TableMapCache.php
new file mode 100644
index 00000000..541b092a
--- /dev/null
+++ b/src/MySQLReplication/Event/RowEvent/TableMapCache.php
@@ -0,0 +1,32 @@
+<?php
+
+declare(strict_types=1);
+
+namespace MySQLReplication\Event\RowEvent;
+
+use Psr\SimpleCache\CacheInterface;
+
+readonly class TableMapCache
+{
+    public function __construct(
+        private CacheInterface $cache
+    ) {
+    }
+
+    public function get(string $tableId): TableMap
+    {
+        /** @var TableMap $tableMap */
+        $tableMap = $this->cache->get($tableId);
+        return $tableMap;
+    }
+
+    public function has(string $tableId): bool
+    {
+        return $this->cache->has($tableId);
+    }
+
+    public function set(string $tableId, TableMap $tableMap): void
+    {
+        $this->cache->set($tableId, $tableMap);
+    }
+}
diff --git a/src/MySQLReplication/Event/XidEvent.php b/src/MySQLReplication/Event/XidEvent.php
index 14777909..41a4a8b6 100644
--- a/src/MySQLReplication/Event/XidEvent.php
+++ b/src/MySQLReplication/Event/XidEvent.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Event;
@@ -9,9 +10,6 @@ class XidEvent extends EventCommon
 {
     public function makeXidDTO(): XidDTO
     {
-        return new XidDTO(
-            $this->eventInfo,
-            $this->binaryDataReader->readUInt64()
-        );
+        return new XidDTO($this->eventInfo, $this->binaryDataReader->readUInt64());
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Exception/MySQLReplicationException.php b/src/MySQLReplication/Exception/MySQLReplicationException.php
index 09d20bed..a4eaf1ee 100644
--- a/src/MySQLReplication/Exception/MySQLReplicationException.php
+++ b/src/MySQLReplication/Exception/MySQLReplicationException.php
@@ -1,50 +1,16 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Exception;
 
-class MySQLReplicationException extends \Exception
-{
-    const SOCKET_DISCONNECTED_MESSAGE = 'Disconnected by remote side.';
-    const SOCKET_DISCONNECTED_CODE = 100;
-    const SOCKET_UNABLE_TO_WRITE_MESSAGE = 'Unable to write to socket: ';
-    const SOCKET_UNABLE_TO_WRITE_CODE = 101;
-    const SOCKET_UNABLE_TO_CREATE_MESSAGE = 'Unable to create socket: ';
-    const SOCKET_UNABLE_TO_CREATE_CODE = 102;
-
-    const INCORRECT_GTID_MESSAGE = 'Incorrect gtid';
-    const INCORRECT_GTID_CODE = 200;
+use Exception;
 
-    const UNKNOWN_JSON_TYPE_MESSAGE = 'Unknown JSON type: ';
-    const UNKNOWN_JSON_TYPE_CODE = 300;
-
-    const IP_ERROR_MESSAGE = 'Incorrect IP given';
-    const IP_ERROR_CODE = 401;
-    const PORT_ERROR_MESSAGE = 'Incorrect port given should be numeric ';
-    const PORT_ERROR_CODE = 402;
-    const DB_NAME_ERROR_MESSAGE = 'Incorrect db name type';
-    const DB_NAME_ERROR_CODE = 404;
-    const CHARSET_ERROR_MESSAGE = 'Incorrect charset type';
-    const CHARSET_ERROR_CODE = 405;
-    const GTID_ERROR_MESSAGE = 'Incorrect gtid';
-    const GTID_ERROR_CODE = 406;
-    const SLAVE_ID_ERROR_MESSAGE = 'Incorrect slave id type';
-    const SLAVE_ID_ERROR_CODE = 407;
-    const BIN_LOG_FILE_NAME_ERROR_MESSAGE = 'Incorrect binlog name';
-    const BIN_LOG_FILE_NAME_ERROR_CODE = 408;
-    const BIN_LOG_FILE_POSITION_ERROR_MESSAGE = 'Incorrect binlog position';
-    const BIN_LOG_FILE_POSITION_ERROR_CODE = 409;
-    const MARIADBGTID_ERROR_MESSAGE = 'Maria gtid must be string';
-    const MARIADBGTID_ERROR_CODE = 410;
-    const TABLE_CACHE_SIZE_ERROR_MESSAGE = 'Table cache must be integer';
-    const TABLE_CACHE_SIZE_ERROR_CODE = 411;
-    const HEARTBEAT_PERIOD_ERROR_MESSAGE = 'Heartbeat period must be integer min:1 max:4294967';
-    const HEARTBEAT_PERIOD_ERROR_CODE = 412;
-
-    const BINLOG_NOT_ENABLED = 'MySQL binary logging is not enabled.';
-    const BINLOG_NOT_ENABLED_CODE = 413;
+class MySQLReplicationException extends Exception
+{
+    public const BINLOG_NOT_ENABLED = 'MySQL binary logging is not enabled.';
+    public const BINLOG_NOT_ENABLED_CODE = 413;
 
-    const DISCONNECTED_MESSAGE = 'Disconnected by remote side';
-    const UNABLE_TO_WRITE_SOCKET = 'Unable to write to socket: ';
-    const UNABLE_TO_CREATE_SOCKET = 'Unable to create socket: ';
-}
\ No newline at end of file
+    public const BINLOG_AUTH_NOT_SUPPORTED = 'MySQL auth plugin is not supported.';
+    public const BINLOG_AUTH_NOT_SUPPORTED_CODE = 414;
+}
diff --git a/src/MySQLReplication/Gtid/Gtid.php b/src/MySQLReplication/Gtid/Gtid.php
index ba92f9c0..f54abb17 100644
--- a/src/MySQLReplication/Gtid/Gtid.php
+++ b/src/MySQLReplication/Gtid/Gtid.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Gtid;
@@ -7,15 +8,16 @@
 
 class Gtid
 {
-    private $intervals = [];
-    private $sid;
+    private array $intervals = [];
+    private string $sid;
 
-    /**
-     * @throws GtidException
-     */
     public function __construct(string $gtid)
     {
-        if (false === (bool)preg_match('/^([0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12})((?::[0-9-]+)+)$/', $gtid, $matches)) {
+        if ((bool)preg_match(
+            '/^([0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12})((?::[0-9-]+)+)$/',
+            $gtid,
+            $matches
+        ) === false) {
             throw new GtidException(GtidException::INCORRECT_GTID_MESSAGE, GtidException::INCORRECT_GTID_CODE);
         }
 
@@ -32,11 +34,10 @@ public function getEncoded(): string
         $buffer .= BinaryDataReader::pack64bit(count($this->intervals));
 
         foreach ($this->intervals as $interval) {
+            $buffer .= BinaryDataReader::pack64bit((int)$interval[0]);
             if (count($interval) !== 1) {
-                $buffer .= BinaryDataReader::pack64bit((int)$interval[0]);
                 $buffer .= BinaryDataReader::pack64bit((int)$interval[1]);
             } else {
-                $buffer .= BinaryDataReader::pack64bit((int)$interval[0]);
                 $buffer .= BinaryDataReader::pack64bit($interval[0] + 1);
             }
         }
@@ -46,6 +47,6 @@ public function getEncoded(): string
 
     public function getEncodedLength(): int
     {
-        return (40 * count($this->intervals));
+        return 40 * count($this->intervals);
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Gtid/GtidCollection.php b/src/MySQLReplication/Gtid/GtidCollection.php
index b51d6a11..fd3cbf05 100644
--- a/src/MySQLReplication/Gtid/GtidCollection.php
+++ b/src/MySQLReplication/Gtid/GtidCollection.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Gtid;
@@ -6,12 +7,12 @@
 use Doctrine\Common\Collections\ArrayCollection;
 use MySQLReplication\BinaryDataReader\BinaryDataReader;
 
+/**
+ * @extends ArrayCollection<int, Gtid>
+ */
 class GtidCollection extends ArrayCollection
 {
-    /**
-     * @throws GtidException
-     */
-    public static function makeCollectionFromString(string $gtids): GtidCollection
+    public static function makeCollectionFromString(string $gtids): self
     {
         $collection = new self();
         foreach (array_filter(explode(',', $gtids)) as $gtid) {
@@ -24,7 +25,6 @@ public static function makeCollectionFromString(string $gtids): GtidCollection
     public function getEncodedLength(): int
     {
         $l = 8;
-        /** @var Gtid $gtid */
         foreach ($this->toArray() as $gtid) {
             $l += $gtid->getEncodedLength();
         }
@@ -35,11 +35,10 @@ public function getEncodedLength(): int
     public function getEncoded(): string
     {
         $s = BinaryDataReader::pack64bit($this->count());
-        /** @var Gtid $gtid */
         foreach ($this->toArray() as $gtid) {
             $s .= $gtid->getEncoded();
         }
 
         return $s;
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Gtid/GtidException.php b/src/MySQLReplication/Gtid/GtidException.php
index f3da2548..bfa50f95 100644
--- a/src/MySQLReplication/Gtid/GtidException.php
+++ b/src/MySQLReplication/Gtid/GtidException.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Gtid;
@@ -7,4 +8,6 @@
 
 class GtidException extends MySQLReplicationException
 {
-}
\ No newline at end of file
+    public const INCORRECT_GTID_MESSAGE = 'Incorrect gtid';
+    public const INCORRECT_GTID_CODE = 200;
+}
diff --git a/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderException.php b/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderException.php
index 8b8a9989..27f765c4 100644
--- a/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderException.php
+++ b/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderException.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\JsonBinaryDecoder;
@@ -7,4 +8,6 @@
 
 class JsonBinaryDecoderException extends MySQLReplicationException
 {
-}
\ No newline at end of file
+    public const UNKNOWN_JSON_TYPE_MESSAGE = 'Unknown JSON type: ';
+    public const UNKNOWN_JSON_TYPE_CODE = 300;
+}
diff --git a/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderFormatter.php b/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderFormatter.php
index b4640a59..5ca0f922 100644
--- a/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderFormatter.php
+++ b/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderFormatter.php
@@ -1,41 +1,28 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\JsonBinaryDecoder;
 
 class JsonBinaryDecoderFormatter
 {
-    public $jsonString = '';
+    public string $jsonString = '';
 
     public function formatValueBool(bool $bool): void
     {
         $this->jsonString .= var_export($bool, true);
     }
 
-    public function formatValueNumeric(int $val): void
+    public function formatValueNumeric(int|string|null|float|bool $val): void
     {
         $this->jsonString .= $val;
     }
 
-    public function formatValue($val): void
+    public function formatValue(int|string|null|float|bool $val): void
     {
         $this->jsonString .= '"' . self::escapeJsonString($val) . '"';
     }
 
-    /**
-     * Some characters needs to be escaped
-     * @see http://www.json.org/
-     * @see https://stackoverflow.com/questions/1048487/phps-json-encode-does-not-escape-all-json-control-characters
-     */
-    private static function escapeJsonString($value): string
-    {
-        return str_replace(
-            ["\\", '/', '"', "\n", "\r", "\t", "\x08", "\x0c"],
-            ["\\\\", "\\/", "\\\"", "\\n", "\\r", "\\t", "\\f", "\\b"],
-            (string)$value
-        );
-    }
-
     public function formatEndObject(): void
     {
         $this->jsonString .= '}';
@@ -75,4 +62,18 @@ public function getJsonString(): string
     {
         return $this->jsonString;
     }
-}
\ No newline at end of file
+
+    /**
+     * Some characters need to be escaped
+     * @see http://www.json.org/
+     * @see https://stackoverflow.com/questions/1048487/phps-json-encode-does-not-escape-all-json-control-characters
+     */
+    private static function escapeJsonString(int|string|null|float|bool $value): string
+    {
+        return str_replace(
+            ['\\', '/', '"', "\n", "\r", "\t", "\x08", "\x0c"],
+            ['\\\\', '\\/', '\\"', '\\n', '\\r', '\\t', '\\f', '\\b'],
+            (string)$value
+        );
+    }
+}
diff --git a/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderService.php b/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderService.php
index a44dee12..d669ef46 100644
--- a/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderService.php
+++ b/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderService.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\JsonBinaryDecoder;
@@ -6,77 +7,84 @@
 use InvalidArgumentException;
 use LengthException;
 use MySQLReplication\BinaryDataReader\BinaryDataReader;
-use MySQLReplication\BinaryDataReader\BinaryDataReaderException;
 
 /**
  * @see https://github.com/mysql/mysql-server/blob/5.7/sql/json_binary.cc
  * @see https://github.com/mysql/mysql-server/blob/8.0/sql/json_binary.cc
  * @see https://github.com/shyiko/mysql-binlog-connector-java/blob/master/src/main/java/com/github/shyiko/mysql/binlog/event/deserialization/json/JsonBinary.java
  */
-class JsonBinaryDecoderService
+readonly class JsonBinaryDecoderService
 {
     public const SMALL_OBJECT = 0;
+
     public const LARGE_OBJECT = 1;
+
     public const SMALL_ARRAY = 2;
+
     public const LARGE_ARRAY = 3;
+
     public const LITERAL = 4;
+
     public const INT16 = 5;
+
     public const UINT16 = 6;
+
     public const INT32 = 7;
+
     public const UINT32 = 8;
+
     public const INT64 = 9;
+
     public const UINT64 = 10;
+
     public const DOUBLE = 11;
+
     public const STRING = 12;
     //public const OPAQUE = 15;
 
     public const LITERAL_NULL = 0;
+
     public const LITERAL_TRUE = 1;
+
     public const LITERAL_FALSE = 2;
 
     public const SMALL_OFFSET_SIZE = 2;
+
     public const LARGE_OFFSET_SIZE = 4;
 
     public const KEY_ENTRY_SIZE_SMALL = 2 + self::SMALL_OFFSET_SIZE;
+
     public const KEY_ENTRY_SIZE_LARGE = 2 + self::LARGE_OFFSET_SIZE;
 
     public const VALUE_ENTRY_SIZE_SMALL = 1 + self::SMALL_OFFSET_SIZE;
+
     public const VALUE_ENTRY_SIZE_LARGE = 1 + self::LARGE_OFFSET_SIZE;
 
     public const OBJECT = 1;
+
     public const ARRAY = 2;
+
     public const SCALAR = 3;
 
-    private $binaryDataReader;
-    private $jsonBinaryDecoderFormatter;
-    private $dataLength;
+    private int $dataLength;
 
     public function __construct(
-        BinaryDataReader $binaryDataReader,
-        JsonBinaryDecoderFormatter $jsonBinaryDecoderFormatter
+        private BinaryDataReader $binaryDataReader,
+        private JsonBinaryDecoderFormatter $jsonBinaryDecoderFormatter
     ) {
-        $this->binaryDataReader = $binaryDataReader;
-        $this->jsonBinaryDecoderFormatter = $jsonBinaryDecoderFormatter;
         $this->dataLength = $this->binaryDataReader->getBinaryDataLength();
     }
 
     public static function makeJsonBinaryDecoder(string $data): self
     {
-        return new self(
-            new BinaryDataReader($data),
-            new JsonBinaryDecoderFormatter()
-        );
+        return new self(new BinaryDataReader($data), new JsonBinaryDecoderFormatter());
     }
 
-    /**
-     * @throws BinaryDataReaderException
-     * @throws JsonBinaryDecoderException
-     */
     public function parseToString(): string
     {
         // Sometimes, we can insert a NULL JSON even we set the JSON field as NOT NULL.
         // If we meet this case, we can return a 'null' value.
-        if($this->binaryDataReader->getBinaryDataLength() === 0) {
+        if ($this->binaryDataReader->getBinaryDataLength() === 0) {
             return 'null';
         }
         $this->parseJson($this->binaryDataReader->readUInt8());
@@ -84,80 +92,66 @@ public function parseToString(): string
         return $this->jsonBinaryDecoderFormatter->getJsonString();
     }
 
-    /**
-     * @throws JsonBinaryDecoderException
-     * @throws BinaryDataReaderException
-     */
     private function parseJson(int $type): void
     {
         $results = [];
-        if (self::SMALL_OBJECT === $type) {
+        if ($type === self::SMALL_OBJECT) {
             $results[self::OBJECT] = $this->parseArrayOrObject(self::OBJECT, self::SMALL_OFFSET_SIZE);
-        } else if (self::LARGE_OBJECT === $type) {
+        } elseif ($type === self::LARGE_OBJECT) {
             $results[self::OBJECT] = $this->parseArrayOrObject(self::OBJECT, self::LARGE_OFFSET_SIZE);
-        } else if (self::SMALL_ARRAY === $type) {
+        } elseif ($type === self::SMALL_ARRAY) {
             $results[self::ARRAY] = $this->parseArrayOrObject(self::ARRAY, self::SMALL_OFFSET_SIZE);
-        } else if (self::LARGE_ARRAY === $type) {
+        } elseif ($type === self::LARGE_ARRAY) {
             $results[self::ARRAY] = $this->parseArrayOrObject(self::ARRAY, self::LARGE_OFFSET_SIZE);
         } else {
             $results[self::SCALAR][] = [
                 'name' => null,
-                'value' => $this->parseScalar($type)
+                'value' => $this->parseScalar($type),
             ];
         }
 
         $this->parseToJson($results);
     }
 
-    /**
-     * @throws BinaryDataReaderException
-     * @throws JsonBinaryDecoderException
-     */
     private function parseToJson(array $results): void
     {
         foreach ($results as $dataType => $entities) {
-            if (self::OBJECT === $dataType) {
+            if ($dataType === self::OBJECT) {
                 $this->jsonBinaryDecoderFormatter->formatBeginObject();
-            } else if (self::ARRAY === $dataType) {
+            } elseif ($dataType === self::ARRAY) {
                 $this->jsonBinaryDecoderFormatter->formatBeginArray();
             }
 
             foreach ($entities as $i => $entity) {
                 if ($dataType === self::SCALAR) {
-
-                    if (null === $entity['value']->getValue()) {
+                    if ($entity['value']->value === null) {
                         $this->jsonBinaryDecoderFormatter->formatValue('null');
-                    } else if (is_bool($entity['value']->getValue())) {
-                        $this->jsonBinaryDecoderFormatter->formatValueBool($entity['value']->getValue());
+                    } elseif (is_bool($entity['value']->value)) {
+                        $this->jsonBinaryDecoderFormatter->formatValueBool($entity['value']->value);
                     } else {
-                        $this->jsonBinaryDecoderFormatter->formatValue($entity['value']->getValue());
+                        $this->jsonBinaryDecoderFormatter->formatValue($entity['value']->value);
                     }
                     continue;
                 }
 
-
                 if ($i !== 0) {
                     $this->jsonBinaryDecoderFormatter->formatNextEntry();
                 }
 
-                if (null !== $entity['name']) {
+                if ($entity['name'] !== null) {
                     $this->jsonBinaryDecoderFormatter->formatName($entity['name']);
                 }
                 $this->assignValues($entity['value']);
             }
 
-            if (self::OBJECT === $dataType) {
+            if ($dataType === self::OBJECT) {
                 $this->jsonBinaryDecoderFormatter->formatEndObject();
-            } else if (self::ARRAY === $dataType) {
+            } elseif ($dataType === self::ARRAY) {
                 $this->jsonBinaryDecoderFormatter->formatEndArray();
             }
         }
     }
 
-    /**
-     * @throws BinaryDataReaderException
-     * @throws JsonBinaryDecoderException
-     */
     private function parseArrayOrObject(int $type, int $intSize): array
     {
         $large = $intSize === self::LARGE_OFFSET_SIZE;
@@ -217,7 +211,7 @@ private function parseArrayOrObject(int $type, int $intSize): array
         for ($i = 0; $i !== $elementCount; ++$i) {
             $results[] = [
                 'name' => $keys[$i] ?? null,
-                'value' => $entries[$i]
+                'value' => $entries[$i],
             ];
         }
 
@@ -239,10 +233,6 @@ private static function valueEntrySize(bool $large): int
         return $large ? self::VALUE_ENTRY_SIZE_LARGE : self::VALUE_ENTRY_SIZE_SMALL;
     }
 
-    /**
-     * @throws BinaryDataReaderException
-     * @throws JsonBinaryDecoderException
-     */
     private function getOffsetOrInLinedValue(int $bytes, int $intSize, int $valueEntrySize): JsonBinaryDecoderValue
     {
         $type = $this->binaryDataReader->readUInt8();
@@ -252,7 +242,7 @@ private function getOffsetOrInLinedValue(int $bytes, int $intSize, int $valueEnt
 
             // In binlog format, JSON arrays are fixed width elements, even though type value can be smaller.
             // In order to properly process this case, we need to move cursor to the next element, which is on position 1 + $valueEntrySize (1 is length of type)
-            if($type === self::UINT16 || $type === self::INT16) {
+            if ($type === self::UINT16 || $type === self::INT16) {
                 $readNextBytes = $valueEntrySize - 2 - 1;
                 $this->binaryDataReader->read($readNextBytes);
             }
@@ -272,39 +262,30 @@ private function getOffsetOrInLinedValue(int $bytes, int $intSize, int $valueEnt
 
     private static function isInLinedType(int $type, int $intSize): bool
     {
-        switch ($type) {
-            case self::LITERAL:
-            case self::INT16:
-            case self::UINT16:
-                return true;
-            case self::INT32:
-            case self::UINT32:
-                return self::LARGE_OFFSET_SIZE === $intSize;
-            default:
-                return false;
-        }
+        return match ($type) {
+            self::LITERAL, self::INT16, self::UINT16 => true,
+            self::INT32, self::UINT32 => $intSize === self::LARGE_OFFSET_SIZE,
+            default => false,
+        };
     }
 
-    /**
-     * @throws JsonBinaryDecoderException
-     */
     private function parseScalar(int $type): JsonBinaryDecoderValue
     {
-        if (self::LITERAL === $type) {
+        if ($type === self::LITERAL) {
             $data = $this->readLiteral();
-        } else if (self::INT16 === $type) {
+        } elseif ($type === self::INT16) {
             $data = $this->binaryDataReader->readInt16();
-        } else if (self::INT32 === $type) {
+        } elseif ($type === self::INT32) {
             $data = ($this->binaryDataReader->readInt32());
-        } else if (self::INT64 === $type) {
+        } elseif ($type === self::INT64) {
             $data = $this->binaryDataReader->readInt64();
-        } else if (self::UINT16 === $type) {
+        } elseif ($type === self::UINT16) {
             $data = ($this->binaryDataReader->readUInt16());
-        } else if (self::UINT64 === $type) {
+        } elseif ($type === self::UINT64) {
             $data = ($this->binaryDataReader->readUInt64());
-        } else if (self::DOUBLE === $type) {
+        } elseif ($type === self::DOUBLE) {
             $data = ($this->binaryDataReader->readDouble());
-        } else if (self::STRING === $type) {
+        } elseif ($type === self::STRING) {
             $data = ($this->binaryDataReader->read($this->readVariableInt()));
         } /**
          * else if (self::OPAQUE === $type)
@@ -325,13 +306,13 @@ private function parseScalar(int $type): JsonBinaryDecoderValue
     private function readLiteral(): ?bool
     {
         $literal = ord($this->binaryDataReader->read(BinaryDataReader::UNSIGNED_SHORT_LENGTH));
-        if (self::LITERAL_NULL === $literal) {
+        if ($literal === self::LITERAL_NULL) {
             return null;
         }
-        if (self::LITERAL_TRUE === $literal) {
+        if ($literal === self::LITERAL_TRUE) {
             return true;
         }
-        if (self::LITERAL_FALSE === $literal) {
+        if ($literal === self::LITERAL_FALSE) {
             return false;
         }
 
@@ -355,27 +336,23 @@ private function readVariableInt(): int
         return $len;
     }
 
-    /**
-     * @throws JsonBinaryDecoderException
-     * @throws BinaryDataReaderException
-     */
     private function assignValues(JsonBinaryDecoderValue $jsonBinaryDecoderValue): void
     {
-        if (false === $jsonBinaryDecoderValue->isIsResolved()) {
-            $this->ensureOffset($jsonBinaryDecoderValue->getOffset());
-            $this->parseJson($jsonBinaryDecoderValue->getType());
-        } else if (null === $jsonBinaryDecoderValue->getValue()) {
+        if ($jsonBinaryDecoderValue->isResolved === false) {
+            $this->ensureOffset($jsonBinaryDecoderValue->offset);
+            $this->parseJson($jsonBinaryDecoderValue->type);
+        } elseif ($jsonBinaryDecoderValue->value === null) {
             $this->jsonBinaryDecoderFormatter->formatValueNull();
-        } else if (is_bool($jsonBinaryDecoderValue->getValue())) {
-            $this->jsonBinaryDecoderFormatter->formatValueBool($jsonBinaryDecoderValue->getValue());
-        } else if (is_numeric($jsonBinaryDecoderValue->getValue())) {
-            $this->jsonBinaryDecoderFormatter->formatValueNumeric($jsonBinaryDecoderValue->getValue());
+        } elseif (is_bool($jsonBinaryDecoderValue->value)) {
+            $this->jsonBinaryDecoderFormatter->formatValueBool($jsonBinaryDecoderValue->value);
+        } elseif (is_numeric($jsonBinaryDecoderValue->value)) {
+            $this->jsonBinaryDecoderFormatter->formatValueNumeric($jsonBinaryDecoderValue->value);
         }
     }
 
     private function ensureOffset(?int $ensureOffset): void
     {
-        if (null === $ensureOffset) {
+        if ($ensureOffset === null) {
             return;
         }
         $pos = $this->binaryDataReader->getReadBytes();
diff --git a/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderValue.php b/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderValue.php
index 34c4fff5..89805284 100644
--- a/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderValue.php
+++ b/src/MySQLReplication/JsonBinaryDecoder/JsonBinaryDecoderValue.php
@@ -1,44 +1,16 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\JsonBinaryDecoder;
 
-class JsonBinaryDecoderValue
+readonly class JsonBinaryDecoderValue
 {
-    private $isResolved;
-    private $value;
-    private $type;
-    private $offset;
-
     public function __construct(
-        bool $isResolved,
-        $value,
-        int $type,
-        int $offset = null
+        public bool $isResolved,
+        public mixed $value,
+        public int $type,
+        public ?int $offset = null
     ) {
-        $this->isResolved = $isResolved;
-        $this->value = $value;
-        $this->type = $type;
-        $this->offset = $offset;
-    }
-
-    public function getOffset(): ?int
-    {
-        return $this->offset;
-    }
-
-    public function getValue()
-    {
-        return $this->value;
-    }
-
-    public function isIsResolved(): bool
-    {
-        return $this->isResolved;
-    }
-
-    public function getType(): int
-    {
-        return $this->type;
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/MySQLReplicationFactory.php b/src/MySQLReplication/MySQLReplicationFactory.php
index a9e9c835..59e96042 100644
--- a/src/MySQLReplication/MySQLReplicationFactory.php
+++ b/src/MySQLReplication/MySQLReplicationFactory.php
@@ -1,89 +1,83 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication;
 
 use Doctrine\DBAL\Connection;
-use Doctrine\DBAL\Exception;
 use Doctrine\DBAL\DriverManager;
-use MySQLReplication\BinaryDataReader\BinaryDataReaderException;
-use MySQLReplication\BinLog\BinLogException;
+use MySQLReplication\BinLog\BinLogServerInfo;
 use MySQLReplication\BinLog\BinLogSocketConnect;
 use MySQLReplication\Cache\ArrayCache;
 use MySQLReplication\Config\Config;
-use MySQLReplication\Config\ConfigException;
 use MySQLReplication\Event\Event;
+use MySQLReplication\Event\RowEvent\RowEventBuilder;
 use MySQLReplication\Event\RowEvent\RowEventFactory;
-use MySQLReplication\Exception\MySQLReplicationException;
-use MySQLReplication\Gtid\GtidException;
-use MySQLReplication\JsonBinaryDecoder\JsonBinaryDecoderException;
 use MySQLReplication\Repository\MySQLRepository;
 use MySQLReplication\Repository\RepositoryInterface;
 use MySQLReplication\Socket\Socket;
-use MySQLReplication\Socket\SocketException;
 use MySQLReplication\Socket\SocketInterface;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
 use Psr\SimpleCache\CacheInterface;
-use Psr\SimpleCache\InvalidArgumentException;
 use Symfony\Component\EventDispatcher\EventDispatcher;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 class MySQLReplicationFactory
 {
-    private $connection;
-    private $eventDispatcher;
-    private $event;
-
-    /**
-     * @throws BinLogException
-     * @throws ConfigException
-     * @throws Exception
-     * @throws SocketException
-     * @throws GtidException
-     */
+    private ?Connection $connection = null;
+    private EventDispatcherInterface $eventDispatcher;
+    private Event $event;
+    private BinLogSocketConnect $binLogSocketConnect;
+
     public function __construct(
         Config $config,
         RepositoryInterface $repository = null,
         CacheInterface $cache = null,
         EventDispatcherInterface $eventDispatcher = null,
-        SocketInterface $socket = null
+        SocketInterface $socket = null,
+        LoggerInterface $logger = null
     ) {
-        $config::validate();
+        $config->validate();
 
-        if (null === $repository) {
+        if ($repository === null) {
             $this->connection = DriverManager::getConnection(
                 [
-                    'user' => Config::getUser(),
-                    'password' => Config::getPassword(),
-                    'host' => Config::getHost(),
-                    'port' => Config::getPort(),
+                    'user' => $config->user,
+                    'password' => $config->password,
+                    'host' => $config->host,
+                    'port' => $config->port,
                     'driver' => 'pdo_mysql',
-                    'charset' => Config::getCharset()
+                    'charset' => $config->charset,
                 ]
             );
             $repository = new MySQLRepository($this->connection);
         }
-        if (null === $cache) {
-            $cache = new ArrayCache();
-        }
+
+        $cache = $cache ?: new ArrayCache($config->tableCacheSize);
+        $logger = $logger ?: new NullLogger();
+        $socket = $socket ?: new Socket();
 
         $this->eventDispatcher = $eventDispatcher ?: new EventDispatcher();
 
-        if (null === $socket) {
-            $socket = new Socket();
-        }
+        $this->binLogSocketConnect = new BinLogSocketConnect($repository, $socket, $logger, $config);
 
         $this->event = new Event(
-            new BinLogSocketConnect(
-                $repository,
-                $socket
-            ),
+            $this->binLogSocketConnect,
             new RowEventFactory(
-                $repository,
-                $cache
+                new RowEventBuilder(
+                    $repository,
+                    $cache,
+                    $config,
+                    $this->binLogSocketConnect->getBinLogServerInfo(),
+                    $logger
+                )
             ),
             $this->eventDispatcher,
-            $cache
+            $cache,
+            $config,
+            $this->binLogSocketConnect->getBinLogServerInfo()
         );
     }
 
@@ -97,36 +91,26 @@ public function unregisterSubscriber(EventSubscriberInterface $eventSubscribers)
         $this->eventDispatcher->removeSubscriber($eventSubscribers);
     }
 
-    public function getDbConnection(): Connection
+    public function getDbConnection(): ?Connection
     {
         return $this->connection;
     }
 
-    /**
-     * @throws SocketException
-     * @throws JsonBinaryDecoderException
-     * @throws BinaryDataReaderException
-     * @throws BinLogException
-     * @throws InvalidArgumentException
-     * @throws MySQLReplicationException
-     */
     public function run(): void
     {
+        /** @phpstan-ignore-next-line */
         while (1) {
             $this->consume();
         }
     }
 
-    /**
-     * @throws MySQLReplicationException
-     * @throws InvalidArgumentException
-     * @throws BinLogException
-     * @throws BinaryDataReaderException
-     * @throws JsonBinaryDecoderException
-     * @throws SocketException
-     */
     public function consume(): void
     {
         $this->event->consume();
     }
+
+    public function getServerInfo(): BinLogServerInfo
+    {
+        return $this->binLogSocketConnect->getBinLogServerInfo();
+    }
 }
diff --git a/src/MySQLReplication/Repository/FieldDTO.php b/src/MySQLReplication/Repository/FieldDTO.php
index 69752994..27bf5f26 100644
--- a/src/MySQLReplication/Repository/FieldDTO.php
+++ b/src/MySQLReplication/Repository/FieldDTO.php
@@ -1,31 +1,19 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Repository;
 
-class FieldDTO
+readonly class FieldDTO
 {
-    private $columnName;
-    private $collationName;
-    private $characterSetName;
-    private $columnComment;
-    private $columnType;
-    private $columnKey;
-
     public function __construct(
-        string $columnName,
-        ?string $collationName,
-        ?string $characterSetName,
-        string $columnComment,
-        string $columnType,
-        string $columnKey
+        public string $columnName,
+        public ?string $collationName,
+        public ?string $characterSetName,
+        public string $columnComment,
+        public string $columnType,
+        public string $columnKey
     ) {
-        $this->columnName = $columnName;
-        $this->collationName = $collationName;
-        $this->characterSetName = $characterSetName;
-        $this->columnComment = $columnComment;
-        $this->columnType = $columnType;
-        $this->columnKey = $columnKey;
     }
 
     public static function makeDummy(int $index): self
@@ -37,7 +25,7 @@ public static function makeDummy(int $index): self
                 'CHARACTER_SET_NAME' => null,
                 'COLUMN_COMMENT' => '',
                 'COLUMN_TYPE' => 'BLOB',
-                'COLUMN_KEY' => ''
+                'COLUMN_KEY' => '',
             ]
         );
     }
@@ -53,34 +41,4 @@ public static function makeFromArray(array $field): self
             $field['COLUMN_KEY']
         );
     }
-
-    public function getColumnName(): string
-    {
-        return $this->columnName;
-    }
-
-    public function getCollationName(): ?string
-    {
-        return $this->collationName;
-    }
-
-    public function getCharacterSetName(): ?string
-    {
-        return $this->characterSetName;
-    }
-
-    public function getColumnComment(): string
-    {
-        return $this->columnComment;
-    }
-
-    public function getColumnType(): string
-    {
-        return $this->columnType;
-    }
-
-    public function getColumnKey(): string
-    {
-        return $this->columnKey;
-    }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Repository/FieldDTOCollection.php b/src/MySQLReplication/Repository/FieldDTOCollection.php
index af7e755e..1d719e8c 100644
--- a/src/MySQLReplication/Repository/FieldDTOCollection.php
+++ b/src/MySQLReplication/Repository/FieldDTOCollection.php
@@ -1,10 +1,14 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Repository;
 
 use Doctrine\Common\Collections\ArrayCollection;
 
+/**
+ * @extends ArrayCollection<int, FieldDTO>
+ */
 class FieldDTOCollection extends ArrayCollection
 {
     public static function makeFromArray(array $fields): self
@@ -16,4 +20,4 @@ public static function makeFromArray(array $fields): self
 
         return $collection;
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Repository/MasterStatusDTO.php b/src/MySQLReplication/Repository/MasterStatusDTO.php
index 678774b0..1e20ecd0 100644
--- a/src/MySQLReplication/Repository/MasterStatusDTO.php
+++ b/src/MySQLReplication/Repository/MasterStatusDTO.php
@@ -1,36 +1,19 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Repository;
 
-class MasterStatusDTO
+readonly class MasterStatusDTO
 {
-    private $position;
-    private $file;
-
     public function __construct(
-        int $position,
-        string $file
+        public string $position,
+        public string $file
     ) {
-        $this->position = $position;
-        $this->file = $file;
     }
 
     public static function makeFromArray(array $data): self
     {
-        return new self(
-            (int)$data['Position'],
-            (string)$data['File']
-        );
-    }
-
-    public function getPosition(): int
-    {
-        return $this->position;
-    }
-
-    public function getFile(): string
-    {
-        return $this->file;
+        return new self((string)$data['Position'], (string)$data['File']);
     }
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Repository/MySQLRepository.php b/src/MySQLReplication/Repository/MySQLRepository.php
index 5bcedf14..ee7a779f 100644
--- a/src/MySQLReplication/Repository/MySQLRepository.php
+++ b/src/MySQLReplication/Repository/MySQLRepository.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Repository;
@@ -8,13 +9,11 @@
 use MySQLReplication\BinLog\BinLogException;
 use MySQLReplication\Exception\MySQLReplicationException;
 
-class MySQLRepository implements RepositoryInterface, PingableConnection
+readonly class MySQLRepository implements RepositoryInterface, PingableConnection
 {
-    private $connection;
-
-    public function __construct(Connection $connection)
-    {
-        $this->connection = $connection;
+    public function __construct(
+        private Connection $connection
+    ) {
     }
 
     public function __destruct()
@@ -42,25 +41,16 @@ public function getFields(string $database, string $table): FieldDTOCollection
                 ORDINAL_POSITION        
        ';
 
-        return FieldDTOCollection::makeFromArray($this->getConnection()->fetchAllAssociative($sql, [$database, $table]));
-    }
-
-    private function getConnection(): Connection
-    {
-        if (false === $this->ping($this->connection)) {
-            $this->connection->close();
-            $this->connection->connect();
-        }
-
-        return $this->connection;
+        return FieldDTOCollection::makeFromArray(
+            $this->getConnection()
+                ->fetchAllAssociative($sql, [$database, $table])
+        );
     }
 
-    /**
-     * @throws Exception
-     */
     public function isCheckSum(): bool
     {
-        $res = $this->getConnection()->fetchAssociative('SHOW GLOBAL VARIABLES LIKE "BINLOG_CHECKSUM"');
+        $res = $this->getConnection()
+            ->fetchAssociative('SHOW GLOBAL VARIABLES LIKE "BINLOG_CHECKSUM"');
 
         return isset($res['Value']) && $res['Value'] !== 'NONE';
     }
@@ -68,24 +58,20 @@ public function isCheckSum(): bool
     public function getVersion(): string
     {
         $r = '';
-        $versions = $this->getConnection()->fetchAllAssociative('SHOW VARIABLES LIKE "version%"');
-        if (is_array($versions) && 0 !== count($versions)) {
-            foreach ($versions as $version) {
-                $r .= $version['Value'];
-            }
+        $versions = $this->getConnection()
+            ->fetchAllAssociative('SHOW VARIABLES LIKE "version%"');
+
+        foreach ($versions as $version) {
+            $r .= $version['Value'];
         }
 
         return $r;
     }
 
-    /**
-     * @inheritDoc
-     * @throws Exception
-     * @throws BinLogException
-     */
     public function getMasterStatus(): MasterStatusDTO
     {
-        $data = $this->getConnection()->fetchAssociative('SHOW MASTER STATUS');
+        $data = $this->getConnection()
+            ->fetchAssociative('SHOW MASTER STATUS');
         if (empty($data)) {
             throw new BinLogException(
                 MySQLReplicationException::BINLOG_NOT_ENABLED,
@@ -101,8 +87,18 @@ public function ping(Connection $connection): bool
         try {
             $connection->executeQuery($connection->getDatabasePlatform()->getDummySelectSQL());
             return true;
-        } catch (Exception $e) {
+        } catch (Exception) {
             return false;
         }
     }
+
+    private function getConnection(): Connection
+    {
+        if ($this->ping($this->connection) === false) {
+            $this->connection->close();
+            $this->connection->connect();
+        }
+
+        return $this->connection;
+    }
 }
diff --git a/src/MySQLReplication/Repository/PingableConnection.php b/src/MySQLReplication/Repository/PingableConnection.php
index c6e77ff7..90b42c85 100644
--- a/src/MySQLReplication/Repository/PingableConnection.php
+++ b/src/MySQLReplication/Repository/PingableConnection.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Repository;
@@ -13,8 +14,6 @@ interface PingableConnection
     /**
      * Pings the database server to determine if the connection is still
      * available. Return true/false based on if that was successful or not.
-     *
-     * @return bool
      */
     public function ping(Connection $connection): bool;
 }
diff --git a/src/MySQLReplication/Repository/RepositoryInterface.php b/src/MySQLReplication/Repository/RepositoryInterface.php
index 0bb86f19..3cbdda55 100644
--- a/src/MySQLReplication/Repository/RepositoryInterface.php
+++ b/src/MySQLReplication/Repository/RepositoryInterface.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Repository;
@@ -12,4 +13,4 @@ public function isCheckSum(): bool;
     public function getVersion(): string;
 
     public function getMasterStatus(): MasterStatusDTO;
-}
\ No newline at end of file
+}
diff --git a/src/MySQLReplication/Socket/Socket.php b/src/MySQLReplication/Socket/Socket.php
index c61ecd66..6b890386 100644
--- a/src/MySQLReplication/Socket/Socket.php
+++ b/src/MySQLReplication/Socket/Socket.php
@@ -1,34 +1,31 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Socket;
 
+use Socket as NativeSocket;
+
 class Socket implements SocketInterface
 {
-    private $socket;
+    private NativeSocket $socket;
 
     public function __destruct()
     {
-        if ($this->isConnected()) {
-            socket_shutdown($this->socket);
-            socket_close($this->socket);
-        }
-    }
-
-    public function isConnected(): bool
-    {
-        return is_resource($this->socket);
+        socket_shutdown($this->socket);
+        socket_close($this->socket);
     }
 
     public function connectToStream(string $host, int $port): void
     {
-        $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
-        if (!$this->socket) {
+        $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
+        if ($socket === false) {
             throw new SocketException(
                 SocketException::SOCKET_UNABLE_TO_CREATE_MESSAGE . $this->getSocketErrorMessage(),
                 SocketException::SOCKET_UNABLE_TO_CREATE_CODE
             );
         }
+        $this->socket = $socket;
         socket_set_block($this->socket);
         socket_set_option($this->socket, SOL_SOCKET, SO_KEEPALIVE, 1);
 
@@ -37,16 +34,6 @@ public function connectToStream(string $host, int $port): void
         }
     }
 
-    private function getSocketErrorMessage(): string
-    {
-        return socket_strerror($this->getSocketErrorCode());
-    }
-
-    private function getSocketErrorCode(): int
-    {
-        return socket_last_error();
-    }
-
     public function readFromSocket(int $length): string
     {
         $received = socket_recv($this->socket, $buf, $length, MSG_WAITALL);
@@ -55,7 +42,7 @@ public function readFromSocket(int $length): string
         }
 
         // http://php.net/manual/en/function.socket-recv.php#47182
-        if (0 === $received) {
+        if ($received === 0) {
             throw new SocketException(
                 SocketException::SOCKET_DISCONNECTED_MESSAGE,
                 SocketException::SOCKET_DISCONNECTED_CODE
@@ -74,4 +61,14 @@ public function writeToSocket(string $data): void
             );
         }
     }
-}
\ No newline at end of file
+
+    private function getSocketErrorMessage(): string
+    {
+        return socket_strerror($this->getSocketErrorCode());
+    }
+
+    private function getSocketErrorCode(): int
+    {
+        return socket_last_error();
+    }
+}
diff --git a/src/MySQLReplication/Socket/SocketException.php b/src/MySQLReplication/Socket/SocketException.php
index 869e04c6..6fd45a9e 100644
--- a/src/MySQLReplication/Socket/SocketException.php
+++ b/src/MySQLReplication/Socket/SocketException.php
@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Socket;
@@ -7,4 +8,10 @@
 
 class SocketException extends MySQLReplicationException
 {
-}
\ No newline at end of file
+    public const SOCKET_DISCONNECTED_MESSAGE = 'Disconnected by remote side.';
+    public const SOCKET_DISCONNECTED_CODE = 100;
+    public const SOCKET_UNABLE_TO_WRITE_MESSAGE = 'Unable to write to socket: ';
+    public const SOCKET_UNABLE_TO_WRITE_CODE = 101;
+    public const SOCKET_UNABLE_TO_CREATE_MESSAGE = 'Unable to create socket: ';
+    public const SOCKET_UNABLE_TO_CREATE_CODE = 102;
+}
diff --git a/src/MySQLReplication/Socket/SocketInterface.php b/src/MySQLReplication/Socket/SocketInterface.php
index b8c62723..9825c0a5 100644
--- a/src/MySQLReplication/Socket/SocketInterface.php
+++ b/src/MySQLReplication/Socket/SocketInterface.php
@@ -1,24 +1,14 @@
 <?php
+
 declare(strict_types=1);
 
 namespace MySQLReplication\Socket;
 
 interface SocketInterface
 {
-    public function isConnected(): bool;
-
-    /**
-     * @throws SocketException
-     */
     public function connectToStream(string $host, int $port): void;
 
-    /**
-     * @throws SocketException
-     */
     public function readFromSocket(int $length): string;
 
-    /**
-     * @throws SocketException
-     */
     public function writeToSocket(string $data): void;
-}
\ No newline at end of file
+}
diff --git a/tests/Integration/BaseTest.php b/tests/Integration/BaseCase.php
similarity index 65%
rename from tests/Integration/BaseTest.php
rename to tests/Integration/BaseCase.php
index 1411e80a..a4c11eab 100644
--- a/tests/Integration/BaseTest.php
+++ b/tests/Integration/BaseCase.php
@@ -1,4 +1,5 @@
 <?php
+
 /** @noinspection PhpUnhandledExceptionInspection */
 
 declare(strict_types=1);
@@ -11,41 +12,25 @@
 use MySQLReplication\Event\DTO\EventDTO;
 use MySQLReplication\Event\DTO\FormatDescriptionEventDTO;
 use MySQLReplication\Event\DTO\QueryDTO;
+use MySQLReplication\Event\DTO\RotateDTO;
 use MySQLReplication\Event\DTO\TableMapDTO;
 use MySQLReplication\MySQLReplicationFactory;
 use PHPUnit\Framework\TestCase;
+use RuntimeException;
 
-abstract class BaseTest extends TestCase
+abstract class BaseCase extends TestCase
 {
-    /**
-     * @var MySQLReplicationFactory
-     */
-    protected $mySQLReplicationFactory;
-    /**
-     * @var Connection
-     */
-    protected $connection;
-    /**
-     * @var string
-     */
-    protected $database = 'mysqlreplication_test';
-    /**
-     * @var EventDTO
-     */
-    protected $currentEvent;
-    /**
-     * @var ConfigBuilder
-     */
-    protected $configBuilder;
-    /**
-     * @var TestEventSubscribers
-     */
-    private $testEventSubscribers;
+    protected ?MySQLReplicationFactory $mySQLReplicationFactory;
 
-    public function setEvent(EventDTO $eventDTO): void
-    {
-        $this->currentEvent = $eventDTO;
-    }
+    protected Connection $connection;
+
+    protected string $database = 'mysqlreplication_test';
+
+    protected ?EventDTO $currentEvent;
+
+    protected ConfigBuilder $configBuilder;
+
+    private TestEventSubscribers $testEventSubscribers;
 
     protected function setUp(): void
     {
@@ -53,26 +38,44 @@ protected function setUp(): void
 
         $this->configBuilder = (new ConfigBuilder())
             ->withUser('root')
-            ->withHost('127.0.0.1')
+            ->withHost('0.0.0.0')
             ->withPassword('root')
             ->withPort(3306)
-            ->withEventsIgnore([ConstEventType::GTID_LOG_EVENT]);
+            ->withEventsIgnore([ConstEventType::GTID_LOG_EVENT->value]);
 
         $this->connect();
 
+        if ($this->mySQLReplicationFactory?->getServerInfo()->versionRevision >= 8 && $this->mySQLReplicationFactory?->getServerInfo()->isGeneric()) {
+            self::assertInstanceOf(RotateDTO::class, $this->getEvent());
+        }
         self::assertInstanceOf(FormatDescriptionEventDTO::class, $this->getEvent());
         self::assertInstanceOf(QueryDTO::class, $this->getEvent());
         self::assertInstanceOf(QueryDTO::class, $this->getEvent());
     }
 
+    protected function tearDown(): void
+    {
+        parent::tearDown();
+
+        $this->disconnect();
+    }
+
+    public function setEvent(EventDTO $eventDTO): void
+    {
+        $this->currentEvent = $eventDTO;
+    }
+
     public function connect(): void
     {
         $this->mySQLReplicationFactory = new MySQLReplicationFactory($this->configBuilder->build());
         $this->testEventSubscribers = new TestEventSubscribers($this);
         $this->mySQLReplicationFactory->registerSubscriber($this->testEventSubscribers);
 
-        $this->connection = $this->mySQLReplicationFactory->getDbConnection();
-
+        $connection = $this->mySQLReplicationFactory->getDbConnection();
+        if ($connection === null) {
+            throw new RuntimeException('Connection not initialized');
+        }
+        $this->connection = $connection;
         $this->connection->executeStatement('SET SESSION time_zone = "UTC"');
         $this->connection->executeStatement('DROP DATABASE IF EXISTS ' . $this->database);
         $this->connection->executeStatement('CREATE DATABASE ' . $this->database);
@@ -82,33 +85,33 @@ public function connect(): void
 
     protected function getEvent(): EventDTO
     {
+        if ($this->mySQLReplicationFactory === null) {
+            throw new RuntimeException('MySQLReplicationFactory not initialized');
+        }
+
         // events can be null lets us continue until we find event
         $this->currentEvent = null;
-        while (1) {
+        while ($this->currentEvent === null) {
             $this->mySQLReplicationFactory->consume();
-            if (null !== $this->currentEvent) {
-                return $this->currentEvent;
-            }
         }
-    }
-
-    protected function tearDown(): void
-    {
-        parent::tearDown();
-
-        $this->disconnect();
+        /** @phpstan-ignore-next-line */
+        return $this->currentEvent;
     }
 
     protected function disconnect(): void
     {
+        if ($this->mySQLReplicationFactory === null) {
+            return;
+        }
         $this->mySQLReplicationFactory->unregisterSubscriber($this->testEventSubscribers);
         $this->mySQLReplicationFactory = null;
-        $this->connection = null;
     }
 
     protected function checkForVersion(float $version): bool
     {
-        return (float)$this->connection->fetchOne('SELECT VERSION()') < $version;
+        /** @phpstan-ignore-next-line */
+        return $this->mySQLReplicationFactory->getServerInfo()
+                ->versionRevision < $version;
     }
 
     protected function createAndInsertValue(string $createQuery, string $insertQuery): EventDTO
diff --git a/tests/Integration/BasicTest.php b/tests/Integration/BasicTest.php
index 43dcd6a1..c3b81cea 100644
--- a/tests/Integration/BasicTest.php
+++ b/tests/Integration/BasicTest.php
@@ -1,4 +1,5 @@
 <?php
+
 /** @noinspection PhpPossiblePolymorphicInvocationInspection */
 
 /** @noinspection PhpUnhandledExceptionInspection */
@@ -7,7 +8,6 @@
 
 namespace MySQLReplication\Tests\Integration;
 
-use MySQLReplication\BinLog\BinLogServerInfo;
 use MySQLReplication\Definitions\ConstEventType;
 use MySQLReplication\Event\DTO\DeleteRowsDTO;
 use MySQLReplication\Event\DTO\FormatDescriptionEventDTO;
@@ -18,15 +18,13 @@
 use MySQLReplication\Event\DTO\WriteRowsDTO;
 use MySQLReplication\Event\DTO\XidDTO;
 use MySQLReplication\MySQLReplicationFactory;
+use RuntimeException;
 use Symfony\Component\EventDispatcher\EventDispatcher;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
-class BasicTest extends BaseTest
+class BasicTest extends BaseCase
 {
-    /**
-     * @test
-     */
-    public function shouldGetDeleteEvent(): void
+    public function testShouldGetDeleteEvent(): void
     {
         $this->createAndInsertValue(
             'CREATE TABLE test (id INT NOT NULL AUTO_INCREMENT, data VARCHAR (50) NOT NULL, PRIMARY KEY (id))',
@@ -42,14 +40,11 @@ public function shouldGetDeleteEvent(): void
         /** @var DeleteRowsDTO $event */
         $event = $this->getEvent();
         self::assertInstanceOf(DeleteRowsDTO::class, $event);
-        self::assertEquals(1, $event->getValues()[0]['id']);
-        self::assertEquals('Hello World', $event->getValues()[0]['data']);
+        self::assertEquals(1, $event->values[0]['id']);
+        self::assertEquals('Hello World', $event->values[0]['data']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldGetUpdateEvent(): void
+    public function testShouldGetUpdateEvent(): void
     {
         $this->createAndInsertValue(
             'CREATE TABLE test (id INT NOT NULL AUTO_INCREMENT, data VARCHAR (50) NOT NULL, PRIMARY KEY (id))',
@@ -65,16 +60,13 @@ public function shouldGetUpdateEvent(): void
         /** @var UpdateRowsDTO $event */
         $event = $this->getEvent();
         self::assertInstanceOf(UpdateRowsDTO::class, $event);
-        self::assertEquals(1, $event->getValues()[0]['before']['id']);
-        self::assertEquals('Hello', $event->getValues()[0]['before']['data']);
-        self::assertEquals(2, $event->getValues()[0]['after']['id']);
-        self::assertEquals('World', $event->getValues()[0]['after']['data']);
+        self::assertEquals(1, $event->values[0]['before']['id']);
+        self::assertEquals('Hello', $event->values[0]['before']['data']);
+        self::assertEquals(2, $event->values[0]['after']['id']);
+        self::assertEquals('World', $event->values[0]['after']['data']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldGetWriteEventDropTable(): void
+    public function testShouldGetWriteEventDropTable(): void
     {
         $this->connection->executeStatement($createExpected = 'CREATE TABLE `test` (id INTEGER(11))');
         $this->connection->executeStatement('INSERT INTO `test` VALUES (1)');
@@ -83,12 +75,12 @@ public function shouldGetWriteEventDropTable(): void
         /** @var QueryDTO $event */
         $event = $this->getEvent();
         self::assertInstanceOf(QueryDTO::class, $event);
-        self::assertEquals($createExpected, $event->getQuery());
+        self::assertEquals($createExpected, $event->query);
 
         /** @var QueryDTO $event */
         $event = $this->getEvent();
         self::assertInstanceOf(QueryDTO::class, $event);
-        self::assertEquals('BEGIN', $event->getQuery());
+        self::assertEquals('BEGIN', $event->query);
 
         /** @var TableMapDTO $event */
         self::assertInstanceOf(TableMapDTO::class, $this->getEvent());
@@ -96,21 +88,18 @@ public function shouldGetWriteEventDropTable(): void
         /** @var WriteRowsDTO $event */
         $event = $this->getEvent();
         self::assertInstanceOf(WriteRowsDTO::class, $event);
-        self::assertEquals([], $event->getValues());
-        self::assertEquals(0, $event->getChangedRows());
+        self::assertEquals([], $event->values);
+        self::assertEquals(0, $event->changedRows);
 
         self::assertInstanceOf(XidDTO::class, $this->getEvent());
 
         /** @var QueryDTO $event */
         $event = $this->getEvent();
         self::assertInstanceOf(QueryDTO::class, $event);
-        self::assertStringContainsString($dropExpected, $event->getQuery());
+        self::assertStringContainsString($dropExpected, $event->query);
     }
 
-    /**
-     * @test
-     */
-    public function shouldGetQueryEventCreateTable(): void
+    public function testShouldGetQueryEventCreateTable(): void
     {
         $this->connection->executeStatement(
             $createExpected = 'CREATE TABLE test (id INT NOT NULL AUTO_INCREMENT, data VARCHAR (50) NOT NULL, PRIMARY KEY (id))'
@@ -119,18 +108,15 @@ public function shouldGetQueryEventCreateTable(): void
         /** @var QueryDTO $event */
         $event = $this->getEvent();
         self::assertInstanceOf(QueryDTO::class, $event);
-        self::assertEquals($createExpected, $event->getQuery());
+        self::assertEquals($createExpected, $event->query);
     }
 
-    /**
-     * @test
-     */
-    public function shouldDropColumn(): void
+    public function testShouldDropColumn(): void
     {
         $this->disconnect();
 
         $this->configBuilder->withEventsOnly(
-            [ConstEventType::WRITE_ROWS_EVENT_V1, ConstEventType::WRITE_ROWS_EVENT_V2]
+            [ConstEventType::WRITE_ROWS_EVENT_V1->value, ConstEventType::WRITE_ROWS_EVENT_V2->value]
         );
 
         $this->connect();
@@ -143,39 +129,40 @@ public function shouldDropColumn(): void
         /** @var WriteRowsDTO $event */
         $event = $this->getEvent();
         self::assertInstanceOf(WriteRowsDTO::class, $event);
-        self::assertEquals(['id' => 1, 'DROPPED_COLUMN_1' => null], $event->getValues()[0]);
+        self::assertEquals([
+            'id' => 1,
+            'DROPPED_COLUMN_1' => null,
+        ], $event->values[0]);
 
         $event = $this->getEvent();
         self::assertInstanceOf(WriteRowsDTO::class, $event);
-        self::assertEquals(['id' => 2], $event->getValues()[0]);
+        self::assertEquals([
+            'id' => 2,
+        ], $event->values[0]);
     }
 
-    /**
-     * @test
-     */
-    public function shouldFilterEvents(): void
+    public function testShouldFilterEvents(): void
     {
         $this->disconnect();
 
-        $this->configBuilder->withEventsOnly([ConstEventType::QUERY_EVENT]);
+        $this->configBuilder->withEventsOnly([ConstEventType::QUERY_EVENT->value]);
 
         $this->connect();
 
         self::assertInstanceOf(QueryDTO::class, $this->getEvent());
         self::assertInstanceOf(QueryDTO::class, $this->getEvent());
 
-        $this->connection->executeStatement($createTableExpected = 'CREATE TABLE test (id INTEGER(11), data VARCHAR(50))');
+        $this->connection->executeStatement(
+            $createTableExpected = 'CREATE TABLE test (id INTEGER(11), data VARCHAR(50))'
+        );
 
         /** @var QueryDTO $event */
         $event = $this->getEvent();
         self::assertInstanceOf(QueryDTO::class, $event);
-        self::assertEquals($createTableExpected, $event->getQuery());
+        self::assertEquals($createTableExpected, $event->query);
     }
 
-    /**
-     * @test
-     */
-    public function shouldFilterTables(): void
+    public function testShouldFilterTables(): void
     {
         $expectedTable = 'test_2';
         $expectedValue = 'foobar';
@@ -184,7 +171,7 @@ public function shouldFilterTables(): void
 
         $this->configBuilder
             ->withEventsOnly(
-                [ConstEventType::WRITE_ROWS_EVENT_V1, ConstEventType::WRITE_ROWS_EVENT_V2]
+                [ConstEventType::WRITE_ROWS_EVENT_V1->value, ConstEventType::WRITE_ROWS_EVENT_V2->value]
             )->withTablesOnly([$expectedTable]);
 
         $this->connect();
@@ -205,20 +192,15 @@ public function shouldFilterTables(): void
 
         $event = $this->getEvent();
         self::assertInstanceOf(WriteRowsDTO::class, $event);
-        self::assertEquals($expectedTable, $event->getTableMap()->getTable());
-        self::assertEquals($expectedValue, $event->getValues()[0]['data']);
+        self::assertEquals($expectedTable, $event->tableMap->table);
+        self::assertEquals($expectedValue, $event->values[0]['data']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldTruncateTable(): void
+    public function testShouldTruncateTable(): void
     {
         $this->disconnect();
 
-        $this->configBuilder->withEventsOnly(
-            [ConstEventType::QUERY_EVENT]
-        );
+        $this->configBuilder->withEventsOnly([ConstEventType::QUERY_EVENT->value]);
 
         $this->connect();
 
@@ -230,19 +212,16 @@ public function shouldTruncateTable(): void
         $this->connection->executeStatement('TRUNCATE TABLE test_truncate_column');
 
         $event = $this->getEvent();
-        self::assertSame('CREATE TABLE test_truncate_column (id INTEGER(11), data VARCHAR(50))', $event->getQuery());
+        self::assertSame('CREATE TABLE test_truncate_column (id INTEGER(11), data VARCHAR(50))', $event->query);
         $event = $this->getEvent();
-        self::assertSame('BEGIN', $event->getQuery());
+        self::assertSame('BEGIN', $event->query);
         $event = $this->getEvent();
-        self::assertSame('TRUNCATE TABLE test_truncate_column', $event->getQuery());
+        self::assertSame('TRUNCATE TABLE test_truncate_column', $event->query);
     }
 
-    /**
-     * @test
-     */
-    public function shouldJsonSetPartialUpdateWithHoles(): void
+    public function testShouldJsonSetPartialUpdateWithHoles(): void
     {
-        if ($this->checkForVersion(5.7) || BinLogServerInfo::isMariaDb()) {
+        if ($this->checkForVersion(5.7) || $this->mySQLReplicationFactory?->getServerInfo()->isMariaDb()) {
             self::markTestIncomplete('Only for mysql 5.7 or higher');
         }
 
@@ -263,22 +242,16 @@ public function shouldJsonSetPartialUpdateWithHoles(): void
         $event = $this->getEvent();
 
         self::assertInstanceOf(UpdateRowsDTO::class, $event);
-        self::assertEquals(
-            $expected,
-            $event->getValues()[0]['before']['j']
-        );
+        self::assertEquals($expected, $event->values[0]['before']['j']);
         self::assertEquals(
             '{"age":22,"addr":{"code":100,"detail":{"ab":"970785C8"}},"name":"Alice"}',
-            $event->getValues()[0]['after']['j']
+            $event->values[0]['after']['j']
         );
     }
 
-    /**
-     * @test
-     */
-    public function shouldJsonRemovePartialUpdateWithHoles(): void
+    public function testShouldJsonRemovePartialUpdateWithHoles(): void
     {
-        if ($this->checkForVersion(5.7) || BinLogServerInfo::isMariaDb()) {
+        if ($this->checkForVersion(5.7) || $this->mySQLReplicationFactory?->getServerInfo()->isMariaDb()) {
             self::markTestIncomplete('Only for mysql 5.7 or higher');
         }
 
@@ -299,22 +272,16 @@ public function shouldJsonRemovePartialUpdateWithHoles(): void
         $event = $this->getEvent();
 
         self::assertInstanceOf(UpdateRowsDTO::class, $event);
-        self::assertEquals(
-            $expected,
-            $event->getValues()[0]['before']['j']
-        );
+        self::assertEquals($expected, $event->values[0]['before']['j']);
         self::assertEquals(
             '{"age":22,"addr":{"code":100,"detail":{}},"name":"Alice"}',
-            $event->getValues()[0]['after']['j']
+            $event->values[0]['after']['j']
         );
     }
 
-    /**
-     * @test
-     */
-    public function shouldJsonReplacePartialUpdateWithHoles(): void
+    public function testShouldJsonReplacePartialUpdateWithHoles(): void
     {
-        if ($this->checkForVersion(5.7) || BinLogServerInfo::isMariaDb()) {
+        if ($this->checkForVersion(5.7) || $this->mySQLReplicationFactory?->getServerInfo()->isMariaDb()) {
             self::markTestIncomplete('Only for mysql 5.7 or higher');
         }
 
@@ -335,20 +302,14 @@ public function shouldJsonReplacePartialUpdateWithHoles(): void
         $event = $this->getEvent();
 
         self::assertInstanceOf(UpdateRowsDTO::class, $event);
-        self::assertEquals(
-            $expected,
-            $event->getValues()[0]['before']['j']
-        );
+        self::assertEquals($expected, $event->values[0]['before']['j']);
         self::assertEquals(
             '{"age":22,"addr":{"code":100,"detail":{"ab":"9707"}},"name":"Alice"}',
-            $event->getValues()[0]['after']['j']
+            $event->values[0]['after']['j']
         );
     }
 
-    /**
-     * @test
-     */
-    public function shouldRoteLog(): void
+    public function testShouldRotateLog(): void
     {
         $this->connection->executeStatement('FLUSH LOGS');
 
@@ -356,14 +317,14 @@ public function shouldRoteLog(): void
 
         self::assertMatchesRegularExpression(
             '/^[a-z-]+\.[\d]+$/',
-            $this->getEvent()->getEventInfo()->getBinLogCurrent()->getBinFileName()
+            $this->getEvent()
+                ->getEventInfo()
+                ->binLogCurrent
+                ->getBinFileName()
         );
     }
 
-    /**
-     * @test
-     */
-    public function shouldUseProvidedEventDispatcher(): void
+    public function testShouldUseProvidedEventDispatcher(): void
     {
         $this->disconnect();
 
@@ -381,7 +342,7 @@ public function shouldUseProvidedEventDispatcher(): void
         /** @var QueryDTO $event */
         $event = $this->getEvent();
         self::assertInstanceOf(QueryDTO::class, $event);
-        self::assertEquals($createExpected, $event->getQuery());
+        self::assertEquals($createExpected, $event->query);
     }
 
     private function connectWithProvidedEventDispatcher(EventDispatcherInterface $eventDispatcher): void
@@ -393,14 +354,22 @@ private function connectWithProvidedEventDispatcher(EventDispatcherInterface $ev
             $eventDispatcher
         );
 
-        $this->connection = $this->mySQLReplicationFactory->getDbConnection();
+        $connection = $this->mySQLReplicationFactory->getDbConnection();
+        if ($connection === null) {
+            throw new RuntimeException('Connection not initialized');
+        }
 
+        $this->connection = $connection;
         $this->connection->executeStatement('SET SESSION time_zone = "UTC"');
         $this->connection->executeStatement('DROP DATABASE IF EXISTS ' . $this->database);
         $this->connection->executeStatement('CREATE DATABASE ' . $this->database);
         $this->connection->executeStatement('USE ' . $this->database);
         $this->connection->executeStatement('SET SESSION sql_mode = \'\';');
 
+        if ($this->mySQLReplicationFactory->getServerInfo()->versionRevision >= 8 && $this->mySQLReplicationFactory->getServerInfo()->isGeneric()) {
+            self::assertInstanceOf(RotateDTO::class, $this->getEvent());
+        }
+
         self::assertInstanceOf(FormatDescriptionEventDTO::class, $this->getEvent());
         self::assertInstanceOf(QueryDTO::class, $this->getEvent());
         self::assertInstanceOf(QueryDTO::class, $this->getEvent());
diff --git a/tests/Integration/TestEventSubscribers.php b/tests/Integration/TestEventSubscribers.php
index 11585052..586c62a3 100644
--- a/tests/Integration/TestEventSubscribers.php
+++ b/tests/Integration/TestEventSubscribers.php
@@ -9,15 +9,13 @@
 
 class TestEventSubscribers extends EventSubscribers
 {
-    private $baseTest;
-
-    public function __construct(BaseTest $baseTest)
-    {
-        $this->baseTest = $baseTest;
+    public function __construct(
+        private readonly BaseCase $baseTest
+    ) {
     }
 
     public function allEvents(EventDTO $event): void
     {
         $this->baseTest->setEvent($event);
     }
-}
\ No newline at end of file
+}
diff --git a/tests/Integration/TypesTest.php b/tests/Integration/TypesTest.php
index 1d1d29bc..42a83cee 100644
--- a/tests/Integration/TypesTest.php
+++ b/tests/Integration/TypesTest.php
@@ -1,4 +1,5 @@
 <?php
+
 /** @noinspection PhpUnhandledExceptionInspection */
 
 /** @noinspection PhpPossiblePolymorphicInvocationInspection */
@@ -7,280 +8,218 @@
 
 namespace MySQLReplication\Tests\Integration;
 
-use MySQLReplication\BinLog\BinLogServerInfo;
-
-class TypesTest extends BaseTest
+class TypesTest extends BaseCase
 {
-    /**
-     * @test
-     */
-    public function shouldBeDecimal(): void
+    public function testShouldBeDecimal(): void
     {
         $create_query = 'CREATE TABLE test (test DECIMAL(2,1))';
         $insert_query = 'INSERT INTO test VALUES(4.2)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals(4.2, $event->getValues()[0]['test']);
+        self::assertEquals(4.2, $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeDecimalLongValues(): void
+    public function testShouldBeDecimalLongValues(): void
     {
         $create_query = 'CREATE TABLE test (test DECIMAL(20,10))';
         $insert_query = 'INSERT INTO test VALUES(9000000123.123456)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertSame($expect = '9000000123.1234560000', $value = $event->getValues()[0]['test']);
+        self::assertSame($expect = '9000000123.1234560000', $value = $event->values[0]['test']);
         self::assertSame(strlen($expect), strlen($value));
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeDecimalLongValues2(): void
+    public function testShouldBeDecimalLongValues2(): void
     {
         $create_query = 'CREATE TABLE test (test DECIMAL(20,10))';
         $insert_query = 'INSERT INTO test VALUES(9000000123.0000012345)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('9000000123.0000012345', $event->getValues()[0]['test']);
+        self::assertEquals('9000000123.0000012345', $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeDecimalNegativeValues(): void
+    public function testShouldBeDecimalNegativeValues(): void
     {
         $create_query = 'CREATE TABLE test (test DECIMAL(20,10), test2 DECIMAL(11,4), test3 DECIMAL(40,30))';
         $insert_query = 'INSERT INTO test VALUES(-42000.123456, -51.1234, -51.123456789098765432123456789)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('-42000.1234560000', $event->getValues()[0]['test']);
-        self::assertEquals('-51.1234', $event->getValues()[0]['test2']);
-        self::assertEquals('-51.123456789098765432123456789000', $event->getValues()[0]['test3']);
+        self::assertEquals('-42000.1234560000', $event->values[0]['test']);
+        self::assertEquals('-51.1234', $event->values[0]['test2']);
+        self::assertEquals('-51.123456789098765432123456789000', $event->values[0]['test3']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeDecimalTwoValues(): void
+    public function testShouldBeDecimalTwoValues(): void
     {
         $create_query = 'CREATE TABLE test ( test DECIMAL(2,1), test2 DECIMAL(20,10) )';
         $insert_query = 'INSERT INTO test VALUES(4.2, 42000.123456)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('4.2', $event->getValues()[0]['test']);
-        self::assertEquals('42000.1234560000', $event->getValues()[0]['test2']);
+        self::assertEquals('4.2', $event->values[0]['test']);
+        self::assertEquals('42000.1234560000', $event->values[0]['test2']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeDecimalZeroScale1(): void
+    public function testShouldBeDecimalZeroScale1(): void
     {
         $create_query = 'CREATE TABLE test (test DECIMAL(23,0))';
         $insert_query = 'INSERT INTO test VALUES(10)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('10', $event->getValues()[0]['test']);
+        self::assertEquals('10', $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeDecimalZeroScale2(): void
+    public function testShouldBeDecimalZeroScale2(): void
     {
         $create_query = 'CREATE TABLE test (test DECIMAL(23,0))';
         $insert_query = 'INSERT INTO test VALUES(12345678912345678912345)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('12345678912345678912345', $event->getValues()[0]['test']);
+        self::assertEquals('12345678912345678912345', $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeDecimalZeroScale3(): void
+    public function testShouldBeDecimalZeroScale3(): void
     {
         $create_query = 'CREATE TABLE test (test DECIMAL(23,0))';
         $insert_query = 'INSERT INTO test VALUES(100000.0)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('100000', $event->getValues()[0]['test']);
+        self::assertEquals('100000', $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeDecimalZeroScale4(): void
+    public function testShouldBeDecimalZeroScale4(): void
     {
         $create_query = 'CREATE TABLE test (test DECIMAL(23,0))';
         $insert_query = 'INSERT INTO test VALUES(-100000.0)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('-100000', $event->getValues()[0]['test']);
+        self::assertEquals('-100000', $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeDecimalZeroScale5(): void
+    public function testShouldBeDecimalZeroScale5(): void
     {
         $create_query = 'CREATE TABLE test (test DECIMAL(23,0))';
         $insert_query = 'INSERT INTO test VALUES(-1234567891234567891234)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('-1234567891234567891234', $event->getValues()[0]['test']);
+        self::assertEquals('-1234567891234567891234', $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeTinyInt(): void
+    public function testShouldBeTinyInt(): void
     {
         $create_query = 'CREATE TABLE test (id TINYINT UNSIGNED NOT NULL, test TINYINT)';
         $insert_query = 'INSERT INTO test VALUES(255, -128)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals(255, $event->getValues()[0]['id']);
-        self::assertEquals(-128, $event->getValues()[0]['test']);
+        self::assertEquals(255, $event->values[0]['id']);
+        self::assertEquals(-128, $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeMapsToBooleanTrue(): void
+    public function testShouldBeMapsToBooleanTrue(): void
     {
         $create_query = 'CREATE TABLE test (id TINYINT UNSIGNED NOT NULL, test BOOLEAN)';
         $insert_query = 'INSERT INTO test VALUES(1, TRUE)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals(1, $event->getValues()[0]['id']);
-        self::assertEquals(1, $event->getValues()[0]['test']);
+        self::assertEquals(1, $event->values[0]['id']);
+        self::assertEquals(1, $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeMapsToBooleanFalse(): void
+    public function testShouldBeMapsToBooleanFalse(): void
     {
         $create_query = 'CREATE TABLE test (id TINYINT UNSIGNED NOT NULL, test BOOLEAN)';
         $insert_query = 'INSERT INTO test VALUES(1, FALSE)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals(1, $event->getValues()[0]['id']);
-        self::assertEquals(0, $event->getValues()[0]['test']);
+        self::assertEquals(1, $event->values[0]['id']);
+        self::assertEquals(0, $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeMapsToNone(): void
+    public function testShouldBeMapsToNone(): void
     {
         $create_query = 'CREATE TABLE test (id TINYINT UNSIGNED NOT NULL, test BOOLEAN)';
         $insert_query = 'INSERT INTO test VALUES(1, NULL)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals(1, $event->getValues()[0]['id']);
-        self::assertEquals(null, $event->getValues()[0]['test']);
+        self::assertEquals(1, $event->values[0]['id']);
+        self::assertEquals(null, $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeMapsToShort(): void
+    public function testShouldBeMapsToShort(): void
     {
         $create_query = 'CREATE TABLE test (id SMALLINT UNSIGNED NOT NULL, test SMALLINT)';
         $insert_query = 'INSERT INTO test VALUES(65535, -32768)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals(65535, $event->getValues()[0]['id']);
-        self::assertEquals(-32768, $event->getValues()[0]['test']);
+        self::assertEquals(65535, $event->values[0]['id']);
+        self::assertEquals(-32768, $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeLong(): void
+    public function testShouldBeLong(): void
     {
         $create_query = 'CREATE TABLE test (id INT UNSIGNED NOT NULL, test INT)';
         $insert_query = 'INSERT INTO test VALUES(4294967295, -2147483648)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals(4294967295, $event->getValues()[0]['id']);
-        self::assertEquals(-2147483648, $event->getValues()[0]['test']);
+        self::assertEquals(4294967295, $event->values[0]['id']);
+        self::assertEquals(-2147483648, $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeFloat(): void
+    public function testShouldBeFloat(): void
     {
         $create_query = 'CREATE TABLE test (id FLOAT NOT NULL, test FLOAT)';
         $insert_query = 'INSERT INTO test VALUES(42.42, -84.84)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals(42.42, $event->getValues()[0]['id']);
-        self::assertEquals(-84.84, $event->getValues()[0]['test']);
+        self::assertEquals(42.42, $event->values[0]['id']);
+        self::assertEquals(-84.84, $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeDouble(): void
+    public function testShouldBeDouble(): void
     {
         $create_query = 'CREATE TABLE test (id DOUBLE NOT NULL, test DOUBLE)';
         $insert_query = 'INSERT INTO test VALUES(42.42, -84.84)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals(42.42, $event->getValues()[0]['id']);
-        self::assertEquals(-84.84, $event->getValues()[0]['test']);
+        self::assertEquals(42.42, $event->values[0]['id']);
+        self::assertEquals(-84.84, $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeTimestamp(): void
+    public function testShouldBeTimestamp(): void
     {
         $create_query = 'CREATE TABLE test (test TIMESTAMP);';
         $insert_query = 'INSERT INTO test VALUES("1984-12-03 12:33:07")';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('1984-12-03 12:33:07', $event->getValues()[0]['test']);
+        self::assertEquals('1984-12-03 12:33:07', $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeTimestampMySQL56(): void
+    public function testShouldBeTimestampMySQL56(): void
     {
         /*
          * https://mariadb.com/kb/en/library/microseconds-in-mariadb/
          * MySQL 5.6 introduced microseconds using a slightly different implementation to MariaDB 5.3.
          * Since MariaDB 10.1, MariaDB has defaulted to the MySQL format ...
          */
-        if (BinLogServerInfo::isMariaDb() && $this->checkForVersion(10.1)) {
+        if ($this->mySQLReplicationFactory?->getServerInfo()->isMariaDb() && $this->checkForVersion(10.1)) {
             self::markTestIncomplete('Only for mariadb 10.1 or higher');
         } elseif ($this->checkForVersion(5.6)) {
             self::markTestIncomplete('Only for mysql 5.6 or higher');
@@ -304,213 +243,167 @@ public function shouldBeTimestampMySQL56(): void
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('1984-12-03 12:33:07', $event->getValues()[0]['test0']);
-        self::assertEquals('1984-12-03 12:33:07.100000', $event->getValues()[0]['test1']);
-        self::assertEquals('1984-12-03 12:33:07.120000', $event->getValues()[0]['test2']);
-        self::assertEquals('1984-12-03 12:33:07.123000', $event->getValues()[0]['test3']);
-        self::assertEquals('1984-12-03 12:33:07.123400', $event->getValues()[0]['test4']);
-        self::assertEquals('1984-12-03 12:33:07.123450', $event->getValues()[0]['test5']);
-        self::assertEquals('1984-12-03 12:33:07.123456', $event->getValues()[0]['test6']);
+        self::assertEquals('1984-12-03 12:33:07', $event->values[0]['test0']);
+        self::assertEquals('1984-12-03 12:33:07.100000', $event->values[0]['test1']);
+        self::assertEquals('1984-12-03 12:33:07.120000', $event->values[0]['test2']);
+        self::assertEquals('1984-12-03 12:33:07.123000', $event->values[0]['test3']);
+        self::assertEquals('1984-12-03 12:33:07.123400', $event->values[0]['test4']);
+        self::assertEquals('1984-12-03 12:33:07.123450', $event->values[0]['test5']);
+        self::assertEquals('1984-12-03 12:33:07.123456', $event->values[0]['test6']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeLongLong(): void
+    public function testShouldBeLongLong(): void
     {
         $create_query = 'CREATE TABLE test (id BIGINT UNSIGNED NOT NULL, test BIGINT)';
         $insert_query = 'INSERT INTO test VALUES(18446744073709551615, -9223372036854775808)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('18446744073709551615', $event->getValues()[0]['id']);
-        self::assertEquals('-9223372036854775808', $event->getValues()[0]['test']);
+        self::assertEquals('18446744073709551615', $event->values[0]['id']);
+        self::assertEquals('-9223372036854775808', $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeInt24(): void
+    public function testShouldBeInt24(): void
     {
         $create_query = 'CREATE TABLE test (id MEDIUMINT UNSIGNED NOT NULL, test MEDIUMINT, test2 MEDIUMINT, test3 MEDIUMINT, test4 MEDIUMINT, test5 MEDIUMINT)';
         $insert_query = 'INSERT INTO test VALUES(16777215, 8388607, -8388608, 8, -8, 0)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals(16777215, $event->getValues()[0]['id']);
-        self::assertEquals(8388607, $event->getValues()[0]['test']);
-        self::assertEquals(-8388608, $event->getValues()[0]['test2']);
-        self::assertEquals(8, $event->getValues()[0]['test3']);
-        self::assertEquals(-8, $event->getValues()[0]['test4']);
-        self::assertEquals(0, $event->getValues()[0]['test5']);
+        self::assertEquals(16777215, $event->values[0]['id']);
+        self::assertEquals(8388607, $event->values[0]['test']);
+        self::assertEquals(-8388608, $event->values[0]['test2']);
+        self::assertEquals(8, $event->values[0]['test3']);
+        self::assertEquals(-8, $event->values[0]['test4']);
+        self::assertEquals(0, $event->values[0]['test5']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeDate(): void
+    public function testShouldBeDate(): void
     {
         $create_query = 'CREATE TABLE test (test DATE);';
         $insert_query = 'INSERT INTO test VALUES("1984-12-03")';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('1984-12-03', $event->getValues()[0]['test']);
+        self::assertEquals('1984-12-03', $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeZeroDate(): void
+    public function testShouldBeZeroDate(): void
     {
         $create_query = 'CREATE TABLE test (id INTEGER, test DATE, test2 DATE);';
         $insert_query = 'INSERT INTO test (id, test2) VALUES(1, "0000-01-21")';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertNull($event->getValues()[0]['test']);
-        self::assertNull($event->getValues()[0]['test2']);
+        self::assertNull($event->values[0]['test']);
+        self::assertNull($event->values[0]['test2']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeZeroMonth(): void
+    public function testShouldBeZeroMonth(): void
     {
         $create_query = 'CREATE TABLE test (id INTEGER, test DATE, test2 DATE);';
         $insert_query = 'INSERT INTO test (id, test2) VALUES(1, "2015-00-21")';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertNull($event->getValues()[0]['test']);
-        self::assertNull($event->getValues()[0]['test2']);
+        self::assertNull($event->values[0]['test']);
+        self::assertNull($event->values[0]['test2']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeZeroDay(): void
+    public function testShouldBeZeroDay(): void
     {
         $create_query = 'CREATE TABLE test (id INTEGER, test DATE, test2 DATE);';
         $insert_query = 'INSERT INTO test (id, test2) VALUES(1, "2015-05-00")';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertNull($event->getValues()[0]['test']);
-        self::assertNull($event->getValues()[0]['test2']);
+        self::assertNull($event->values[0]['test']);
+        self::assertNull($event->values[0]['test2']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeTime(): void
+    public function testShouldBeTime(): void
     {
         $create_query = 'CREATE TABLE test (test TIME);';
         $insert_query = 'INSERT INTO test VALUES("12:33:18")';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('12:33:18', $event->getValues()[0]['test']);
+        self::assertEquals('12:33:18', $event->values[0]['test']);
     }
 
-
-    /**
-     * @test
-     */
-    public function shouldBeZeroTime(): void
+    public function testShouldBeZeroTime(): void
     {
         $create_query = 'CREATE TABLE test (id INTEGER, test TIME NOT NULL DEFAULT 0);';
         $insert_query = 'INSERT INTO test (id) VALUES(1)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('00:00:00', $event->getValues()[0]['test']);
+        self::assertEquals('00:00:00', $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeDateTime(): void
+    public function testShouldBeDateTime(): void
     {
         $create_query = 'CREATE TABLE test (test DATETIME);';
         $insert_query = 'INSERT INTO test VALUES("1984-12-03 12:33:07")';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('1984-12-03 12:33:07', $event->getValues()[0]['test']);
+        self::assertEquals('1984-12-03 12:33:07', $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeZeroDateTime(): void
+    public function testShouldBeZeroDateTime(): void
     {
         $create_query = 'CREATE TABLE test (id INTEGER, test DATETIME NOT NULL DEFAULT 0);';
         $insert_query = 'INSERT INTO test (id) VALUES(1)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertNull($event->getValues()[0]['test']);
+        self::assertNull($event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeBrokenDateTime(): void
+    public function testShouldBeBrokenDateTime(): void
     {
         $create_query = 'CREATE TABLE test (test DATETIME NOT NULL);';
         $insert_query = 'INSERT INTO test VALUES("2013-00-00 00:00:00")';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertNull($event->getValues()[0]['test']);
+        self::assertNull($event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldReturnNullOnZeroDateDateTime(): void
+    public function testShouldReturnNullOnZeroDateDateTime(): void
     {
         $create_query = 'CREATE TABLE test (test DATETIME NOT NULL);';
         $insert_query = 'INSERT INTO test VALUES("0000-00-00 00:00:00")';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertNull($event->getValues()[0]['test']);
+        self::assertNull($event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeYear(): void
+    public function testShouldBeYear(): void
     {
         $create_query = 'CREATE TABLE test (test YEAR(4), test2 YEAR, test3 YEAR)';
         $insert_query = 'INSERT INTO test VALUES(1984, 1984, 0000)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals(1984, $event->getValues()[0]['test']);
-        self::assertEquals(1984, $event->getValues()[0]['test2']);
-        self::assertNull($event->getValues()[0]['test3']);
+        self::assertEquals(1984, $event->values[0]['test']);
+        self::assertEquals(1984, $event->values[0]['test2']);
+        self::assertNull($event->values[0]['test3']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeVarChar(): void
+    public function testShouldBeVarChar(): void
     {
         $create_query = 'CREATE TABLE test (test VARCHAR(242)) CHARACTER SET latin1 COLLATE latin1_bin;';
         $insert_query = 'INSERT INTO test VALUES("Hello")';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('Hello', $event->getValues()[0]['test']);
+        self::assertEquals('Hello', $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBe1024CharsLongVarChar(): void
+    public function testShouldBe1024CharsLongVarChar(): void
     {
         $expected = str_repeat('-', 1024);
 
@@ -519,13 +412,10 @@ public function shouldBe1024CharsLongVarChar(): void
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals($expected, $event->getValues()[0]['test']);
+        self::assertEquals($expected, $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeBit(): void
+    public function testShouldBeBit(): void
     {
         $create_query = 'CREATE TABLE test (
             test BIT(6),
@@ -544,20 +434,17 @@ public function shouldBeBit(): void
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('100010', $event->getValues()[0]['test']);
-        self::assertEquals('1000101010111000', $event->getValues()[0]['test2']);
-        self::assertEquals('100010101101', $event->getValues()[0]['test3']);
-        self::assertEquals('101100111', $event->getValues()[0]['test4']);
+        self::assertEquals('100010', $event->values[0]['test']);
+        self::assertEquals('1000101010111000', $event->values[0]['test2']);
+        self::assertEquals('100010101101', $event->values[0]['test3']);
+        self::assertEquals('101100111', $event->values[0]['test4']);
         self::assertEquals(
             '1101011010110100100111100011010100010100101110111011101011011010',
-            $event->getValues()[0]['test5']
+            $event->values[0]['test5']
         );
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeEnum(): void
+    public function testShouldBeEnum(): void
     {
         $create_query = 'CREATE TABLE test
             (
@@ -570,84 +457,68 @@ public function shouldBeEnum(): void
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('ba', $event->getValues()[0]['test']);
-        self::assertEquals('a', $event->getValues()[0]['test2']);
-        self::assertEquals('', $event->getValues()[0]['test3']);
+        self::assertEquals('ba', $event->values[0]['test']);
+        self::assertEquals('a', $event->values[0]['test2']);
+        self::assertEquals('', $event->values[0]['test3']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeSet(): void
+    public function testShouldBeSet(): void
     {
         $create_query = 'CREATE TABLE test (test SET("a", "ba", "c"), test2 SET("a", "ba", "c")) CHARACTER SET latin1 COLLATE latin1_bin;';
         $insert_query = 'INSERT INTO test VALUES("ba,a,c", "a,c")';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals(['a', 'ba', 'c'], $event->getValues()[0]['test']);
-        self::assertEquals(['a', 'c'], $event->getValues()[0]['test2']);
+        self::assertEquals(['a', 'ba', 'c'], $event->values[0]['test']);
+        self::assertEquals(['a', 'c'], $event->values[0]['test2']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeTinyBlob(): void
+    public function testShouldBeTinyBlob(): void
     {
         $create_query = 'CREATE TABLE test (test TINYBLOB, test2 TINYTEXT) CHARACTER SET latin1 COLLATE latin1_bin;';
         $insert_query = 'INSERT INTO test VALUES("Hello", "World")';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('Hello', $event->getValues()[0]['test']);
-        self::assertEquals('World', $event->getValues()[0]['test2']);
+        self::assertEquals('Hello', $event->values[0]['test']);
+        self::assertEquals('World', $event->values[0]['test2']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeMediumBlob(): void
+    public function testShouldBeMediumBlob(): void
     {
         $create_query = 'CREATE TABLE test (test MEDIUMBLOB, test2 MEDIUMTEXT) CHARACTER SET latin1 COLLATE latin1_bin;';
         $insert_query = 'INSERT INTO test VALUES("Hello", "World")';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('Hello', $event->getValues()[0]['test']);
-        self::assertEquals('World', $event->getValues()[0]['test2']);
+        self::assertEquals('Hello', $event->values[0]['test']);
+        self::assertEquals('World', $event->values[0]['test2']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeNullOnBooleanType(): void
+    public function testShouldBeNullOnBooleanType(): void
     {
         $create_query = 'CREATE TABLE test (test BOOLEAN);';
         $insert_query = 'INSERT INTO test VALUES(NULL)';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertNull($event->getValues()[0]['test']);
+        self::assertNull($event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeLongBlob(): void
+    public function testShouldBeLongBlob(): void
     {
         $create_query = 'CREATE TABLE test (test LONGBLOB, test2 LONGTEXT) CHARACTER SET latin1 COLLATE latin1_bin;';
         $insert_query = 'INSERT INTO test VALUES("Hello", "World")';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('Hello', $event->getValues()[0]['test']);
-        self::assertEquals('World', $event->getValues()[0]['test2']);
+        self::assertEquals('Hello', $event->values[0]['test']);
+        self::assertEquals('World', $event->values[0]['test2']);
     }
 
     /**
      * https://dev.mysql.com/doc/internals/en/mysql-packet.html
      * https://dev.mysql.com/doc/internals/en/sending-more-than-16mbyte.html
-     *
      */
     public function shouldBeLongerTextThan16Mb(): void
     {
@@ -659,42 +530,33 @@ public function shouldBeLongerTextThan16Mb(): void
         $insert_query = 'INSERT INTO test (data) VALUES ("' . $long_text_data . '")';
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals(strlen($long_text_data), strlen($event->getValues()[0]['data']));
+        self::assertEquals(strlen($long_text_data), strlen($event->values[0]['data']));
 
         $long_text_data = null;
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeBlob(): void
+    public function testShouldBeBlob(): void
     {
         $create_query = 'CREATE TABLE test (test BLOB, test2 TEXT) CHARACTER SET latin1 COLLATE latin1_bin;';
         $insert_query = 'INSERT INTO test VALUES("Hello", "World")';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('Hello', $event->getValues()[0]['test']);
-        self::assertEquals('World', $event->getValues()[0]['test2']);
+        self::assertEquals('Hello', $event->values[0]['test']);
+        self::assertEquals('World', $event->values[0]['test2']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeString(): void
+    public function testShouldBeString(): void
     {
         $create_query = 'CREATE TABLE test (test CHAR(12)) CHARACTER SET latin1 COLLATE latin1_bin;';
         $insert_query = 'INSERT INTO test VALUES("Hello")';
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals('Hello', $event->getValues()[0]['test']);
+        self::assertEquals('Hello', $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeGeometry(): void
+    public function testShouldBeGeometry(): void
     {
         $prefix = 'ST_';
         if ($this->checkForVersion(5.6)) {
@@ -708,14 +570,11 @@ public function shouldBeGeometry(): void
 
         self::assertEquals(
             '000000000101000000000000000000f03f000000000000f03f',
-            bin2hex($event->getValues()[0]['test'])
+            bin2hex($event->values[0]['test'])
         );
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeNull(): void
+    public function testShouldBeNull(): void
     {
         $create_query = 'CREATE TABLE test (
             test TINYINT NULL DEFAULT NULL,
@@ -743,17 +602,14 @@ public function shouldBeNull(): void
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertNull($event->getValues()[0]['test']);
-        self::assertEquals(-128, $event->getValues()[0]['test2']);
-        self::assertNull($event->getValues()[0]['test3']);
-        self::assertEquals(42, $event->getValues()[0]['test7']);
-        self::assertEquals(84, $event->getValues()[0]['test20']);
+        self::assertNull($event->values[0]['test']);
+        self::assertEquals(-128, $event->values[0]['test2']);
+        self::assertNull($event->values[0]['test3']);
+        self::assertEquals(42, $event->values[0]['test7']);
+        self::assertEquals(84, $event->values[0]['test20']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeEncodedLatin1(): void
+    public function testShouldBeEncodedLatin1(): void
     {
         $this->connection->executeStatement('SET CHARSET latin1');
 
@@ -764,13 +620,10 @@ public function shouldBeEncodedLatin1(): void
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals($string, $event->getValues()[0]['test']);
+        self::assertEquals($string, $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeEncodedUTF8(): void
+    public function testShouldBeEncodedUTF8(): void
     {
         $this->connection->executeStatement('SET CHARSET utf8');
 
@@ -781,15 +634,12 @@ public function shouldBeEncodedUTF8(): void
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        self::assertEquals($string, $event->getValues()[0]['test']);
+        self::assertEquals($string, $event->values[0]['test']);
     }
 
-    /**
-     * @test
-     */
-    public function shouldBeJson(): void
+    public function testShouldBeJson(): void
     {
-        if ($this->checkForVersion(5.7) || BinLogServerInfo::isMariaDb()) {
+        if ($this->checkForVersion(5.7) || $this->mySQLReplicationFactory?->getServerInfo()->isMariaDb()) {
             self::markTestIncomplete('Only for mysql 5.7 or higher');
         }
 
@@ -841,7 +691,7 @@ public function shouldBeJson(): void
 
         $event = $this->createAndInsertValue($create_query, $insert_query);
 
-        $results = $event->getValues();
+        $results = $event->values;
 
         self::assertEquals(null, $results[0]['j']);
         self::assertEquals('{"a":2}', $results[1]['j']);
diff --git a/tests/Unit/BaseTest.php b/tests/Unit/BaseTest.php
deleted file mode 100644
index 0d6eeb2e..00000000
--- a/tests/Unit/BaseTest.php
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace MySQLReplication\Tests\Unit;
-
-use PHPUnit\Framework\TestCase;
-
-abstract class BaseTest extends TestCase
-{
-}
\ No newline at end of file
diff --git a/tests/Unit/BinaryDataReader/BinaryDataReaderTest.php b/tests/Unit/BinaryDataReader/BinaryDataReaderTest.php
index e09910cd..676d3958 100644
--- a/tests/Unit/BinaryDataReader/BinaryDataReaderTest.php
+++ b/tests/Unit/BinaryDataReader/BinaryDataReaderTest.php
@@ -1,4 +1,5 @@
 <?php
+
 /** @noinspection PhpUnhandledExceptionInspection */
 
 declare(strict_types=1);
@@ -7,46 +8,40 @@
 
 use MySQLReplication\BinaryDataReader\BinaryDataReader;
 use MySQLReplication\BinaryDataReader\BinaryDataReaderException;
-use MySQLReplication\Tests\Unit\BaseTest;
+use PHPUnit\Framework\Attributes\DataProvider;
+use PHPUnit\Framework\TestCase;
 
-class BinaryDataReaderTest extends BaseTest
+class BinaryDataReaderTest extends TestCase
 {
-    /**
-     * @test
-     */
-    public function shouldRead(): void
+    public function testShouldRead(): void
     {
         $expected = 'zażółć gęślą jaźń';
         self::assertSame($expected, pack('H*', $this->getBinaryRead(unpack('H*', $expected)[1])->read(52)));
     }
 
-    private function getBinaryRead($data): BinaryDataReader
-    {
-        return new BinaryDataReader($data);
-    }
-
-    /**
-     * @test
-     */
-    public function shouldReadCodedBinary(): void
+    public function testShouldReadCodedBinary(): void
     {
         self::assertSame(0, $this->getBinaryRead(pack('C', ''))->readCodedBinary());
         self::assertNull($this->getBinaryRead(pack('C', BinaryDataReader::NULL_COLUMN))->readCodedBinary());
-        self::assertSame(0, $this->getBinaryRead(pack('i', BinaryDataReader::UNSIGNED_SHORT_COLUMN))->readCodedBinary());
-        self::assertSame(0, $this->getBinaryRead(pack('i', BinaryDataReader::UNSIGNED_INT24_COLUMN))->readCodedBinary());
+        self::assertSame(
+            0,
+            $this->getBinaryRead(pack('i', BinaryDataReader::UNSIGNED_SHORT_COLUMN))->readCodedBinary()
+        );
+        self::assertSame(
+            0,
+            $this->getBinaryRead(pack('i', BinaryDataReader::UNSIGNED_INT24_COLUMN))->readCodedBinary()
+        );
     }
 
-    /**
-     * @test
-     */
-    public function shouldThrowErrorOnUnknownCodedBinary(): void
+    public function testShouldThrowErrorOnUnknownCodedBinary(): void
     {
         $this->expectException(BinaryDataReaderException::class);
 
-        $this->getBinaryRead(pack('i', 255))->readCodedBinary();
+        $this->getBinaryRead(pack('i', 255))
+            ->readCodedBinary();
     }
 
-    public function dataProviderForUInt(): array
+    public static function dataProviderForUInt(): array
     {
         return [
             [1, pack('c', 1), 1],
@@ -55,38 +50,34 @@ public function dataProviderForUInt(): array
             [4, pack('I', 123123543), 123123543],
             [5, pack('CI', 71, 2570258120), 657986078791],
             [6, pack('v3', 2570258120, 2570258120, 2570258120), 7456176998088],
-            [7, pack('CSI', 66, 7890, 2570258120), 43121775657013826]
+            [7, pack('CSI', 66, 7890, 2570258120), 43121775657013826],
         ];
     }
 
-    /**
-     * @test
-     */
-    public function shouldReadReadUInt64(): void
+    public function testShouldReadReadUInt64(): void
     {
-        $this->assertSame('18374686483949813760', $this->getBinaryRead(pack('VV', 4278190080, 4278190080))->readUInt64());
+        $this->assertSame(
+            '18374686483949813760',
+            $this->getBinaryRead(pack('VV', 4278190080, 4278190080))
+                ->readUInt64()
+        );
     }
 
-    /**
-     * @dataProvider dataProviderForUInt
-     * @test
-     */
-    public function shouldReadUIntBySize($size, $data, $expected): void
+    #[DataProvider('dataProviderForUInt')]
+    public function testShouldReadUIntBySize(mixed $size, mixed $data, mixed $expected): void
     {
         self::assertSame($expected, $this->getBinaryRead($data)->readUIntBySize($size));
     }
 
-    /**
-     * @test
-     */
-    public function shouldThrowErrorOnReadUIntBySizeNotSupported(): void
+    public function testShouldThrowErrorOnReadUIntBySizeNotSupported(): void
     {
         $this->expectException(BinaryDataReaderException::class);
 
-        $this->getBinaryRead('')->readUIntBySize(32);
+        $this->getBinaryRead('')
+            ->readUIntBySize(32);
     }
 
-    public function dataProviderForBeInt(): array
+    public static function dataProviderForBeInt(): array
     {
         return [
             [1, pack('c', 4), 4],
@@ -97,130 +88,94 @@ public function dataProviderForBeInt(): array
         ];
     }
 
-    /**
-     * @dataProvider dataProviderForBeInt
-     * @test
-     */
-    public function shouldReadIntBeBySize(int $size, string $data, int $expected): void
-    {
+    #[DataProvider('dataProviderForBeInt')] public function testShouldReadIntBeBySize(
+        int $size,
+        string $data,
+        int $expected
+    ): void {
         self::assertSame($expected, $this->getBinaryRead($data)->readIntBeBySize($size));
     }
 
-    /**
-     * @test
-     */
-    public function shouldThrowErrorOnReadIntBeBySizeNotSupported(): void
+    public function testShouldThrowErrorOnReadIntBeBySizeNotSupported(): void
     {
         $this->expectException(BinaryDataReaderException::class);
 
-        $this->getBinaryRead('')->readIntBeBySize(666);
+        $this->getBinaryRead('')
+            ->readIntBeBySize(666);
     }
 
-    /**
-     * @test
-     */
-    public function shouldReadInt16(): void
+    public function testShouldReadInt16(): void
     {
         $expected = 1000;
         self::assertSame($expected, $this->getBinaryRead(pack('s', $expected))->readInt16());
     }
 
-    /**
-     * @test
-     */
-    public function shouldUnreadAdvance(): void
+    public function testShouldUnreadAdvance(): void
     {
         $binaryDataReader = $this->getBinaryRead('123');
 
-        self::assertEquals('123', $binaryDataReader->getData());
+        self::assertEquals('123', $binaryDataReader->getBinaryData());
         self::assertEquals(0, $binaryDataReader->getReadBytes());
 
         $binaryDataReader->advance(2);
 
-        self::assertEquals('3', $binaryDataReader->getData());
+        self::assertEquals('3', $binaryDataReader->getBinaryData());
         self::assertEquals(2, $binaryDataReader->getReadBytes());
 
         $binaryDataReader->unread('12');
 
-        self::assertEquals('123', $binaryDataReader->getData());
+        self::assertEquals('123', $binaryDataReader->getBinaryData());
         self::assertEquals(0, $binaryDataReader->getReadBytes());
     }
 
-    /**
-     * @test
-     */
-    public function shouldReadInt24(): void
+    public function testShouldReadInt24(): void
     {
         self::assertSame(-6513508, $this->getBinaryRead(pack('C3', -100, -100, -100))->readInt24());
     }
 
-    /**
-     * @test
-     */
-    public function shouldReadInt64(): void
+    public function testShouldReadInt64(): void
     {
         self::assertSame('-72057589759737856', $this->getBinaryRead(pack('VV', 4278190080, 4278190080))->readInt64());
     }
 
-    /**
-     * @test
-     */
-    public function shouldReadLengthCodedPascalString(): void
+    public function testShouldReadLengthCodedPascalString(): void
     {
         $expected = 255;
         self::assertSame(
             $expected,
-            hexdec(
-                bin2hex(
-                    $this->getBinaryRead(pack('cc', 1, $expected))->readLengthString(1)
-                )
-            )
+            hexdec(bin2hex($this->getBinaryRead(pack('cc', 1, $expected))->readLengthString(1)))
         );
     }
 
-    /**
-     * @test
-     */
-    public function shouldReadInt32(): void
+    public function testShouldReadInt32(): void
     {
         $expected = 777333;
         self::assertSame($expected, $this->getBinaryRead(pack('i', $expected))->readInt32());
     }
 
-
-    /**
-     * @test
-     */
-    public function shouldReadFloat(): void
+    public function testShouldReadFloat(): void
     {
         $expected = 0.001;
-        self::assertSame($expected, $this->getBinaryRead(pack('f', $expected))->readFloat());
+        // we need to add round as php have problem with precision in floats
+        self::assertSame($expected, round($this->getBinaryRead(pack('f', $expected))->readFloat(), 3));
     }
 
-    /**
-     * @test
-     */
-    public function shouldReadDouble(): void
+    public function testShouldReadDouble(): void
     {
         $expected = 1321312312.143567586;
         self::assertSame($expected, $this->getBinaryRead(pack('d', $expected))->readDouble());
     }
 
-    /**
-     * @test
-     */
-    public function shouldReadTableId(): void
+    public function testShouldReadTableId(): void
     {
         self::assertSame(
             '7456176998088',
-            $this->getBinaryRead(pack('v3', 2570258120, 2570258120, 2570258120))->readTableId()
+            $this->getBinaryRead(pack('v3', 2570258120, 2570258120, 2570258120))
+                ->readTableId()
         );
     }
 
-    /**
-     * @test
-     */
-    public function shouldCheckIsCompleted(): void
+    public function testShouldCheckIsCompleted(): void
     {
         self::assertFalse($this->getBinaryRead('')->isComplete(1));
 
@@ -229,20 +184,19 @@ public function shouldCheckIsCompleted(): void
         self::assertTrue($r->isComplete(1));
     }
 
-    /**
-     * @test
-     */
-    public function shouldPack64bit(): void
+    public function testShouldPack64bit(): void
     {
         $expected = 9223372036854775807;
         self::assertSame((string)$expected, $this->getBinaryRead(BinaryDataReader::pack64bit($expected))->readInt64());
     }
 
-    /**
-     * @test
-     */
-    public function shouldGetBinaryDataLength(): void
+    public function testShouldGetBinaryDataLength(): void
     {
         self::assertSame(3, $this->getBinaryRead('foo')->getBinaryDataLength());
     }
-}
\ No newline at end of file
+
+    private function getBinaryRead(string $data): BinaryDataReader
+    {
+        return new BinaryDataReader($data);
+    }
+}
diff --git a/tests/Unit/Cache/ArrayCacheTest.php b/tests/Unit/Cache/ArrayCacheTest.php
index 507fa0d1..ad121021 100644
--- a/tests/Unit/Cache/ArrayCacheTest.php
+++ b/tests/Unit/Cache/ArrayCacheTest.php
@@ -1,4 +1,5 @@
 <?php
+
 /** @noinspection PhpUnhandledExceptionInspection */
 
 declare(strict_types=1);
@@ -7,62 +8,48 @@
 
 use MySQLReplication\Cache\ArrayCache;
 use MySQLReplication\Config\ConfigBuilder;
-use MySQLReplication\Tests\Unit\BaseTest;
+use PHPUnit\Framework\TestCase;
 
-class ArrayCacheTest extends BaseTest
+class ArrayCacheTest extends TestCase
 {
     private $arrayCache;
 
-    public function setUp(): void
+    protected function setUp(): void
     {
         parent::setUp();
         $this->arrayCache = new ArrayCache();
     }
 
-    /**
-     * @test
-     */
-    public function shouldGet(): void
+    public function testShouldGet(): void
     {
         $this->arrayCache->set('foo', 'bar');
         self::assertSame('bar', $this->arrayCache->get('foo'));
     }
 
-    /**
-     * @test
-     */
-    public function shouldSet(): void
+    public function testShouldSet(): void
     {
         $this->arrayCache->set('foo', 'bar');
         self::assertSame('bar', $this->arrayCache->get('foo'));
     }
 
-    /**
-     * @test
-     */
-    public function shouldClearCacheOnSet(): void
+    public function testShouldClearCacheOnSet(): void
     {
-        (new ConfigBuilder())->withTableCacheSize(1)->build();
+        (new ConfigBuilder())->withTableCacheSize(1)
+            ->build();
 
         $this->arrayCache->set('foo', 'bar');
         $this->arrayCache->set('foo', 'bar');
         self::assertSame('bar', $this->arrayCache->get('foo'));
     }
 
-    /**
-     * @test
-     */
-    public function shouldDelete(): void
+    public function testShouldDelete(): void
     {
         $this->arrayCache->set('foo', 'bar');
         $this->arrayCache->delete('foo');
         self::assertNull($this->arrayCache->get('foo'));
     }
 
-    /**
-     * @test
-     */
-    public function shouldClear(): void
+    public function testShouldClear(): void
     {
         $this->arrayCache->set('foo', 'bar');
         $this->arrayCache->set('foo1', 'bar1');
@@ -70,41 +57,43 @@ public function shouldClear(): void
         self::assertNull($this->arrayCache->get('foo'));
     }
 
-    /**
-     * @test
-     */
-    public function shouldGetMultiple(): void
+    public function testShouldGetMultiple(): void
     {
-        $expect = ['foo' => 'bar', 'foo1' => 'bar1'];
+        $expect = [
+            'foo' => 'bar',
+            'foo1' => 'bar1',
+        ];
         $this->arrayCache->setMultiple($expect);
-        self::assertSame(['foo' => 'bar'], $this->arrayCache->getMultiple(['foo']));
+        self::assertSame([
+            'foo' => 'bar',
+        ], $this->arrayCache->getMultiple(['foo']));
     }
 
-    /**
-     * @test
-     */
-    public function shouldSetMultiple(): void
+    public function testShouldSetMultiple(): void
     {
-        $expect = ['foo' => 'bar', 'foo1' => 'bar1'];
+        $expect = [
+            'foo' => 'bar',
+            'foo1' => 'bar1',
+        ];
         $this->arrayCache->setMultiple($expect);
         self::assertSame($expect, $this->arrayCache->getMultiple(['foo', 'foo1']));
     }
 
-    /**
-     * @test
-     */
-    public function shouldDeleteMultiple(): void
+    public function testShouldDeleteMultiple(): void
     {
-        $expect = ['foo' => 'bar', 'foo1' => 'bar1', 'foo2' => 'bar2'];
+        $expect = [
+            'foo' => 'bar',
+            'foo1' => 'bar1',
+            'foo2' => 'bar2',
+        ];
         $this->arrayCache->setMultiple($expect);
         $this->arrayCache->deleteMultiple(['foo', 'foo1']);
-        self::assertSame(['foo2' => 'bar2'], $this->arrayCache->getMultiple(['foo2']));
+        self::assertSame([
+            'foo2' => 'bar2',
+        ], $this->arrayCache->getMultiple(['foo2']));
     }
 
-    /**
-     * @test
-     */
-    public function shouldHas(): void
+    public function testShouldHas(): void
     {
         self::assertFalse($this->arrayCache->has('foo'));
         $this->arrayCache->set('foo', 'bar');
diff --git a/tests/Unit/Config/ConfigTest.php b/tests/Unit/Config/ConfigTest.php
index d0b81a30..9e137b18 100644
--- a/tests/Unit/Config/ConfigTest.php
+++ b/tests/Unit/Config/ConfigTest.php
@@ -1,4 +1,5 @@
 <?php
+
 /** @noinspection PhpUnhandledExceptionInspection */
 
 declare(strict_types=1);
@@ -8,9 +9,10 @@
 use MySQLReplication\Config\Config;
 use MySQLReplication\Config\ConfigBuilder;
 use MySQLReplication\Config\ConfigException;
-use MySQLReplication\Tests\Unit\BaseTest;
+use PHPUnit\Framework\Attributes\DataProvider;
+use PHPUnit\Framework\TestCase;
 
-class ConfigTest extends BaseTest
+class ConfigTest extends TestCase
 {
     public function shouldMakeConfig(): void
     {
@@ -23,15 +25,20 @@ public function shouldMakeConfig(): void
             'gtid' => '9b1c8d18-2a76-11e5-a26b-000c2976f3f3:1-177592',
             'slaveId' => 1,
             'binLogFileName' => 'binfile1.bin',
-            'binLogPosition' => 666,
+            'binLogPosition' => '999',
             'eventsOnly' => [],
             'eventsIgnore' => [],
             'tablesOnly' => ['test_table'],
             'databasesOnly' => ['test_database'],
             'mariaDbGtid' => '123:123',
             'tableCacheSize' => 777,
-            'custom' => [['random' => 'data']],
+            'custom' => [
+                [
+                    'random' => 'data',
+                ],
+            ],
             'heartbeatPeriod' => 69,
+            'slaveUuid' => '6c27ed6d-7ee1-11e3-be39-6c626d957cff',
         ];
 
         $config = new Config(
@@ -51,132 +58,139 @@ public function shouldMakeConfig(): void
             $expected['databasesOnly'],
             $expected['tableCacheSize'],
             $expected['custom'],
-            $expected['heartbeatPeriod']
+            $expected['heartbeatPeriod'],
+            $expected['slaveUuid']
         );
 
-        self::assertSame($expected['user'], $config::getUser());
-        self::assertSame($expected['host'], $config::getHost());
-        self::assertSame($expected['port'], $config::getPort());
-        self::assertSame($expected['password'], $config::getPassword());
-        self::assertSame($expected['charset'], $config::getCharset());
-        self::assertSame($expected['gtid'], $config::getGtid());
-        self::assertSame($expected['slaveId'], $config::getSlaveId());
-        self::assertSame($expected['binLogFileName'], $config::getBinLogFileName());
-        self::assertSame($expected['binLogPosition'], $config::getBinLogPosition());
-        self::assertSame($expected['eventsOnly'], $config::getEventsOnly());
-        self::assertSame($expected['eventsIgnore'], $config::getEventsIgnore());
-        self::assertSame($expected['tablesOnly'], $config::getTablesOnly());
-        self::assertSame($expected['mariaDbGtid'], $config::getMariaDbGtid());
-        self::assertSame($expected['tableCacheSize'], $config::getTableCacheSize());
-        self::assertSame($expected['custom'], $config::getCustom());
-        self::assertSame($expected['heartbeatPeriod'], $config::getHeartbeatPeriod());
-        self::assertSame($expected['databasesOnly'], $config::getDatabasesOnly());
-
-        $config::validate();
+        self::assertSame($expected['user'], $config->user);
+        self::assertSame($expected['host'], $config->host);
+        self::assertSame($expected['port'], $config->port);
+        self::assertSame($expected['password'], $config->password);
+        self::assertSame($expected['charset'], $config->charset);
+        self::assertSame($expected['gtid'], $config->gtid);
+        self::assertSame($expected['slaveId'], $config->slaveId);
+        self::assertSame($expected['binLogFileName'], $config->binLogFileName);
+        self::assertSame($expected['binLogPosition'], $config->binLogPosition);
+        self::assertSame($expected['eventsOnly'], $config->eventsOnly);
+        self::assertSame($expected['eventsIgnore'], $config->eventsIgnore);
+        self::assertSame($expected['tablesOnly'], $config->tablesOnly);
+        self::assertSame($expected['mariaDbGtid'], $config->mariaDbGtid);
+        self::assertSame($expected['tableCacheSize'], $config->tableCacheSize);
+        self::assertSame($expected['custom'], $config->custom);
+        self::assertSame($expected['heartbeatPeriod'], $config->heartbeatPeriod);
+        self::assertSame($expected['databasesOnly'], $config->databasesOnly);
+        self::assertSame($expected['slaveUuid'], $config->slaveUuid);
+
+        $config->validate();
     }
 
-    /**
-     * @test
-     */
-    public function shouldCheckDataBasesOnly(): void
+    public function testShouldCheckDataBasesOnly(): void
     {
-        (new ConfigBuilder())->withDatabasesOnly(['boo'])->build();
-        self::assertTrue(Config::checkDataBasesOnly('foo'));
+        $config = (new ConfigBuilder())->withDatabasesOnly(['boo'])->build();
+        self::assertTrue($config->checkDataBasesOnly('foo'));
 
-        (new ConfigBuilder())->withDatabasesOnly(['foo'])->build();
-        self::assertFalse(Config::checkDataBasesOnly('foo'));
+        $config = (new ConfigBuilder())->withDatabasesOnly(['foo'])->build();
+        self::assertFalse($config->checkDataBasesOnly('foo'));
 
-        (new ConfigBuilder())->withDatabasesOnly(['test'])->build();
-        self::assertFalse(Config::checkDataBasesOnly('test'));
+        $config = (new ConfigBuilder())->withDatabasesOnly(['test'])->build();
+        self::assertFalse($config->checkDataBasesOnly('test'));
 
-        (new ConfigBuilder())->withDatabasesOnly(['foo'])->build();
-        self::assertTrue(Config::checkDataBasesOnly('bar'));
+        $config = (new ConfigBuilder())->withDatabasesOnly(['foo'])->build();
+        self::assertTrue($config->checkDataBasesOnly('bar'));
     }
 
-    /**
-     * @test
-     */
-    public function shouldCheckTablesOnly(): void
+    public function testShouldCheckTablesOnly(): void
     {
-        self::assertFalse(Config::checkTablesOnly('foo'));
+        $config = (new ConfigBuilder())->build();
+        self::assertFalse($config->checkTablesOnly('foo'));
 
-        (new ConfigBuilder())->withTablesOnly(['foo'])->build();
-        self::assertFalse(Config::checkTablesOnly('foo'));
+        $config = (new ConfigBuilder())->withTablesOnly(['foo'])->build();
+        self::assertFalse($config->checkTablesOnly('foo'));
 
-        (new ConfigBuilder())->withTablesOnly(['test'])->build();
-        self::assertFalse(Config::checkTablesOnly('test'));
+        $config = (new ConfigBuilder())->withTablesOnly(['test'])->build();
+        self::assertFalse($config->checkTablesOnly('test'));
 
-        (new ConfigBuilder())->withTablesOnly(['foo'])->build();
-        self::assertTrue(Config::checkTablesOnly('bar'));
+        $config = (new ConfigBuilder())->withTablesOnly(['foo'])->build();
+        self::assertTrue($config->checkTablesOnly('bar'));
     }
 
-    /**
-     * @test
-     */
-    public function shouldCheckEvent(): void
+    public function testShouldCheckEvent(): void
     {
-        self::assertTrue(Config::checkEvent(1));
+        $config = (new ConfigBuilder())->build();
+        self::assertTrue($config->checkEvent(1));
 
-        (new ConfigBuilder())->withEventsOnly([2])->build();
-        self::assertTrue(Config::checkEvent(2));
+        $config = (new ConfigBuilder())->withEventsOnly([2])->build();
+        self::assertTrue($config->checkEvent(2));
 
-        (new ConfigBuilder())->withEventsOnly([3])->build();
-        self::assertFalse(Config::checkEvent(4));
+        $config = (new ConfigBuilder())->withEventsOnly([3])->build();
+        self::assertFalse($config->checkEvent(4));
 
-        (new ConfigBuilder())->withEventsIgnore([4])->build();
-        self::assertFalse(Config::checkEvent(4));
+        $config = (new ConfigBuilder())->withEventsIgnore([4])->build();
+        self::assertFalse($config->checkEvent(4));
     }
 
-    public function shouldCheckHeartbeatPeriodProvider(): array
+    public static function shouldCheckHeartbeatPeriodProvider(): array
     {
-        return [
-            [0],
-            [0.0],
-            [0.001],
-            [4294967],
-            [2],
-        ];
+        return [[0], [0.0], [0.001], [4294967], [2]];
     }
 
-    /**
-     * @test
-     * @dataProvider shouldCheckHeartbeatPeriodProvider
-     */
-    public function shouldCheckHeartbeatPeriod($heartbeatPeriod): void
-    {
-        $config = (new ConfigBuilder())->withHeartbeatPeriod($heartbeatPeriod)->build();
-        $config::validate();
+    #[DataProvider('shouldCheckHeartbeatPeriodProvider')] public function testShouldCheckHeartbeatPeriod(
+        int|float $heartbeatPeriod
+    ): void {
+        $config = (new ConfigBuilder())->withHeartbeatPeriod($heartbeatPeriod)
+            ->build();
+        $config->validate();
 
-        self::assertSame((float)$heartbeatPeriod, $config::getHeartbeatPeriod());
+        self::assertEquals($heartbeatPeriod, $config->heartbeatPeriod);
     }
 
-    public function shouldValidateProvider(): array
+    public static function shouldValidateProvider(): array
     {
         return [
             ['host', 'aaa', ConfigException::IP_ERROR_MESSAGE, ConfigException::IP_ERROR_CODE],
             ['port', -1, ConfigException::PORT_ERROR_MESSAGE, ConfigException::PORT_ERROR_CODE],
             ['slaveId', -1, ConfigException::SLAVE_ID_ERROR_MESSAGE, ConfigException::SLAVE_ID_ERROR_CODE],
             ['gtid', '-1', ConfigException::GTID_ERROR_MESSAGE, ConfigException::GTID_ERROR_CODE],
-            ['binLogPosition', -1, ConfigException::BIN_LOG_FILE_POSITION_ERROR_MESSAGE, ConfigException::BIN_LOG_FILE_POSITION_ERROR_CODE],
-            ['tableCacheSize', -1, ConfigException::TABLE_CACHE_SIZE_ERROR_MESSAGE, ConfigException::TABLE_CACHE_SIZE_ERROR_CODE],
-            ['heartbeatPeriod', 4294968, ConfigException::HEARTBEAT_PERIOD_ERROR_MESSAGE, ConfigException::HEARTBEAT_PERIOD_ERROR_CODE],
-            ['heartbeatPeriod', -1, ConfigException::HEARTBEAT_PERIOD_ERROR_MESSAGE, ConfigException::HEARTBEAT_PERIOD_ERROR_CODE],
+            [
+                'binLogPosition',
+                '-1',
+                ConfigException::BIN_LOG_FILE_POSITION_ERROR_MESSAGE,
+                ConfigException::BIN_LOG_FILE_POSITION_ERROR_CODE,
+            ],
+            [
+                'tableCacheSize',
+                -1,
+                ConfigException::TABLE_CACHE_SIZE_ERROR_MESSAGE,
+                ConfigException::TABLE_CACHE_SIZE_ERROR_CODE,
+            ],
+            [
+                'heartbeatPeriod',
+                4294968,
+                ConfigException::HEARTBEAT_PERIOD_ERROR_MESSAGE,
+                ConfigException::HEARTBEAT_PERIOD_ERROR_CODE,
+            ],
+            [
+                'heartbeatPeriod',
+                -1,
+                ConfigException::HEARTBEAT_PERIOD_ERROR_MESSAGE,
+                ConfigException::HEARTBEAT_PERIOD_ERROR_CODE,
+            ],
         ];
     }
 
-    /**
-     * @test
-     * @dataProvider shouldValidateProvider
-     */
-    public function shouldValidate(string $configKey, $configValue, string $expectedMessage, int $expectedCode): void
-    {
+    #[DataProvider('shouldValidateProvider')]
+    public function testShouldValidate(
+        string $configKey,
+        mixed $configValue,
+        string $expectedMessage,
+        int $expectedCode
+    ): void {
         $this->expectException(ConfigException::class);
         $this->expectExceptionMessage($expectedMessage);
         $this->expectExceptionCode($expectedCode);
 
         /** @var Config $config */
         $config = (new ConfigBuilder())->{'with' . strtoupper($configKey)}($configValue)->build();
-        $config::validate();
+        $config->validate();
     }
-
-}
\ No newline at end of file
+}
diff --git a/tests/Unit/Event/RowEvent/TableMapTest.php b/tests/Unit/Event/RowEvent/TableMapTest.php
index 485c2e1f..01cb80a5 100644
--- a/tests/Unit/Event/RowEvent/TableMapTest.php
+++ b/tests/Unit/Event/RowEvent/TableMapTest.php
@@ -6,14 +6,11 @@
 
 use MySQLReplication\Event\RowEvent\ColumnDTOCollection;
 use MySQLReplication\Event\RowEvent\TableMap;
-use MySQLReplication\Tests\Unit\BaseTest;
+use PHPUnit\Framework\TestCase;
 
-class TableMapTest extends BaseTest
+class TableMapTest extends TestCase
 {
-    /**
-     * @test
-     */
-    public function shouldMakeTableMap(): void
+    public function testShouldMakeTableMap(): void
     {
         $expected = [
             'database' => 'foo',
@@ -23,7 +20,6 @@ public function shouldMakeTableMap(): void
             'columnDTOCollection' => new ColumnDTOCollection(),
         ];
 
-
         $tableMap = new TableMap(
             $expected['database'],
             $expected['table'],
@@ -32,14 +28,14 @@ public function shouldMakeTableMap(): void
             $expected['columnDTOCollection']
         );
 
-        self::assertSame($expected['database'], $tableMap->getDatabase());
-        self::assertSame($expected['table'], $tableMap->getTable());
-        self::assertSame($expected['tableId'], $tableMap->getTableId());
-        self::assertSame($expected['columnsAmount'], $tableMap->getColumnsAmount());
-        self::assertSame($expected['columnDTOCollection'], $tableMap->getColumnDTOCollection());
+        self::assertSame($expected['database'], $tableMap->database);
+        self::assertSame($expected['table'], $tableMap->table);
+        self::assertSame($expected['tableId'], $tableMap->tableId);
+        self::assertSame($expected['columnsAmount'], $tableMap->columnsAmount);
+        self::assertSame($expected['columnDTOCollection'], $tableMap->columnDTOCollection);
 
         self::assertInstanceOf(\JsonSerializable::class, $tableMap);
         /** @noinspection JsonEncodingApiUsageInspection */
         self::assertSame(json_encode($expected), json_encode($tableMap));
     }
-}
\ No newline at end of file
+}
diff --git a/tests/Unit/Gtid/GtidCollectionTest.php b/tests/Unit/Gtid/GtidCollectionTest.php
index ac3c7893..919a1123 100644
--- a/tests/Unit/Gtid/GtidCollectionTest.php
+++ b/tests/Unit/Gtid/GtidCollectionTest.php
@@ -6,17 +6,13 @@
 
 use MySQLReplication\Gtid\Gtid;
 use MySQLReplication\Gtid\GtidCollection;
-use MySQLReplication\Gtid\GtidException;
-use MySQLReplication\Tests\Unit\BaseTest;
+use PHPUnit\Framework\TestCase;
 
-class GtidCollectionTest extends BaseTest
+class GtidCollectionTest extends TestCase
 {
-    /**
-     * @var GtidCollection
-     */
-    private $gtidCollection;
+    private GtidCollection $gtidCollection;
 
-    public function setUp(): void
+    protected function setUp(): void
     {
         parent::setUp();
 
@@ -26,18 +22,12 @@ public function setUp(): void
         $this->gtidCollection->add(new Gtid('BBBBBBBB-CCCC-FFFF-DDDD-AAAAAAAAAAAA:1'));
     }
 
-    /**
-     * @test
-     */
-    public function shouldGetEncodedLength(): void
+    public function testShouldGetEncodedLength(): void
     {
         self::assertSame(88, $this->gtidCollection->getEncodedLength());
     }
 
-    /**
-     * @test
-     */
-    public function shouldGetEncoded(): void
+    public function testShouldGetEncoded(): void
     {
         self::assertSame(
             '02000000000000009b1c8d182a7611e5a26b000c2976f3f301000000000000000100000000000000b8b5020000000000bbbbbbbbccccffffddddaaaaaaaaaaaa010000000000000001000000000000000200000000000000',
@@ -45,11 +35,7 @@ public function shouldGetEncoded(): void
         );
     }
 
-    /**
-     * @test
-     * @throws GtidException
-     */
-    public function shouldCreateCollection(): void
+    public function testShouldCreateCollection(): void
     {
         self::assertCount(1, GtidCollection::makeCollectionFromString('9b1c8d18-2a76-11e5-a26b-000c2976f3f3:1-177592'));
     }
diff --git a/tests/Unit/Gtid/GtidTest.php b/tests/Unit/Gtid/GtidTest.php
index ecd4fbfc..34b8dad3 100644
--- a/tests/Unit/Gtid/GtidTest.php
+++ b/tests/Unit/Gtid/GtidTest.php
@@ -1,4 +1,5 @@
 <?php
+
 /** @noinspection PhpUnhandledExceptionInspection */
 
 declare(strict_types=1);
@@ -7,39 +8,28 @@
 
 use MySQLReplication\Gtid\Gtid;
 use MySQLReplication\Gtid\GtidException;
-use MySQLReplication\Tests\Unit\BaseTest;
+use PHPUnit\Framework\TestCase;
 
-class GtidTest extends BaseTest
+class GtidTest extends TestCase
 {
-    /**
-     * @test
-     */
-    public function shouldGetEncoded(): void
-    {
-        self::assertSame('9b1c8d182a7611e5a26b000c2976f3f301000000000000000100000000000000b8b5020000000000', bin2hex($this->getGtid('9b1c8d18-2a76-11e5-a26b-000c2976f3f3:1-177592')->getEncoded()));
-        self::assertSame('9b1c8d182a7611e5a26b000c2976f3f3010000000000000001000000000000000200000000000000', bin2hex($this->getGtid('9b1c8d18-2a76-11e5-a26b-000c2976f3f3:1')->getEncoded()));
-    }
-
-    /**
-     * @throws GtidException
-     */
-    private function getGtid(string $data): Gtid
+    public function testShouldGetEncoded(): void
     {
-        return new Gtid($data);
+        self::assertSame(
+            '9b1c8d182a7611e5a26b000c2976f3f301000000000000000100000000000000b8b5020000000000',
+            bin2hex($this->getGtid('9b1c8d18-2a76-11e5-a26b-000c2976f3f3:1-177592')->getEncoded())
+        );
+        self::assertSame(
+            '9b1c8d182a7611e5a26b000c2976f3f3010000000000000001000000000000000200000000000000',
+            bin2hex($this->getGtid('9b1c8d18-2a76-11e5-a26b-000c2976f3f3:1')->getEncoded())
+        );
     }
 
-    /**
-     * @test
-     */
-    public function shouldGetEncodedLength(): void
+    public function testShouldGetEncodedLength(): void
     {
         self::assertSame(40, $this->getGtid('9b1c8d18-2a76-11e5-a26b-000c2976f3f3:1-177592')->getEncodedLength());
     }
 
-    /**
-     * @test
-     */
-    public function shouldThrowErrorOnIncrrectGtid(): void
+    public function testShouldThrowErrorOnIncrrectGtid(): void
     {
         $this->expectException(GtidException::class);
         $this->expectExceptionMessage(GtidException::INCORRECT_GTID_MESSAGE);
@@ -47,4 +37,9 @@ public function shouldThrowErrorOnIncrrectGtid(): void
 
         $this->getGtid('not gtid');
     }
-}
\ No newline at end of file
+
+    private function getGtid(string $data): Gtid
+    {
+        return new Gtid($data);
+    }
+}
diff --git a/tests/Unit/Repository/MySQLRepositoryTest.php b/tests/Unit/Repository/MySQLRepositoryTest.php
index 4fcc0454..5ad88ac9 100644
--- a/tests/Unit/Repository/MySQLRepositoryTest.php
+++ b/tests/Unit/Repository/MySQLRepositoryTest.php
@@ -1,4 +1,5 @@
 <?php
+
 /** @noinspection PhpUnhandledExceptionInspection */
 
 declare(strict_types=1);
@@ -11,33 +12,26 @@
 use MySQLReplication\Repository\FieldDTOCollection;
 use MySQLReplication\Repository\MasterStatusDTO;
 use MySQLReplication\Repository\MySQLRepository;
-use MySQLReplication\Tests\Unit\BaseTest;
 use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
 
-class MySQLRepositoryTest extends BaseTest
+class MySQLRepositoryTest extends TestCase
 {
-    /**
-     * @var MySQLRepository
-     */
-    private $mySQLRepositoryTest;
-    /**
-     * @var Connection|MockObject
-     */
-    private $connection;
-
-    public function setUp(): void
+    private MySQLRepository $mySQLRepositoryTest;
+
+    private Connection|MockObject $connection;
+
+    protected function setUp(): void
     {
         parent::setUp();
 
         $this->connection = $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock();
-        $this->connection->method('getDatabasePlatform')->willReturn(new MySQLPlatform());
+        $this->connection->method('getDatabasePlatform')
+            ->willReturn(new MySQLPlatform());
         $this->mySQLRepositoryTest = new MySQLRepository($this->connection);
     }
 
-    /**
-     * @test
-     */
-    public function shouldGetFields(): void
+    public function testShouldGetFields(): void
     {
         $expected = [
             [
@@ -46,51 +40,55 @@ public function shouldGetFields(): void
                 'CHARACTER_SET_NAME' => 'charname',
                 'COLUMN_COMMENT' => 'colcommnet',
                 'COLUMN_TYPE' => 'coltype',
-                'COLUMN_KEY' => 'colkey'
-            ]
+                'COLUMN_KEY' => 'colkey',
+            ],
         ];
 
-        $this->connection->method('fetchAllAssociative')->willReturn($expected);
+        $this->connection->method('fetchAllAssociative')
+            ->willReturn($expected);
 
-        self::assertEquals(FieldDTOCollection::makeFromArray($expected), $this->mySQLRepositoryTest->getFields('foo', 'bar'));
+        self::assertEquals(
+            FieldDTOCollection::makeFromArray($expected),
+            $this->mySQLRepositoryTest->getFields('foo', 'bar')
+        );
     }
 
-    /**
-     * @test
-     */
-    public function shouldIsCheckSum(): void
+    public function testShouldIsCheckSum(): void
     {
         self::assertFalse($this->mySQLRepositoryTest->isCheckSum());
 
-        $this->connection->method('fetchAssociative')->willReturnOnConsecutiveCalls(
-            ['Value' => 'CRC32'],
-            ['Value' => 'NONE']
-        );
+        $this->connection->method('fetchAssociative')
+            ->willReturnOnConsecutiveCalls([
+                'Value' => 'CRC32',
+            ], [
+                'Value' => 'NONE',
+            ]);
 
         self::assertTrue($this->mySQLRepositoryTest->isCheckSum());
         self::assertFalse($this->mySQLRepositoryTest->isCheckSum());
     }
 
-    /**
-     * @test
-     */
-    public function shouldGetVersion(): void
+    public function testShouldGetVersion(): void
     {
         $expected = [
-            ['Value' => 'foo'],
-            ['Value' => 'bar'],
-            ['Value' => '123'],
+            [
+                'Value' => 'foo',
+            ],
+            [
+                'Value' => 'bar',
+            ],
+            [
+                'Value' => '123',
+            ],
         ];
 
-        $this->connection->method('fetchAllAssociative')->willReturn($expected);
+        $this->connection->method('fetchAllAssociative')
+            ->willReturn($expected);
 
         self::assertEquals('foobar123', $this->mySQLRepositoryTest->getVersion());
     }
 
-    /**
-     * @test
-     */
-    public function shouldGetMasterStatus(): void
+    public function testShouldGetMasterStatus(): void
     {
         $expected = [
             'File' => 'mysql-bin.000002',
@@ -100,31 +98,19 @@ public function shouldGetMasterStatus(): void
             'Executed_Gtid_Set' => '041de05f-a36a-11e6-bc73-000c2976f3f3:1-8023',
         ];
 
-        $this->connection->method('fetchAssociative')->willReturn($expected);
+        $this->connection->method('fetchAssociative')
+            ->willReturn($expected);
 
         self::assertEquals(MasterStatusDTO::makeFromArray($expected), $this->mySQLRepositoryTest->getMasterStatus());
     }
 
-    /**
-     * @test
-     */
-    public function shouldDestroy(): void
-    {
-        $this->mySQLRepositoryTest = null;
-        self::assertTrue(true);
-    }
-
-    /**
-     * @test
-     */
-    public function shouldReconnect(): void
+    public function testShouldReconnect(): void
     {
         // just to cover private getConnection
-        $this->connection->method('executeQuery')->willReturnCallback(
-            static function () {
+        $this->connection->method('executeQuery')
+            ->willReturnCallback(static function () {
                 throw new Exception('');
-            }
-        );
+            });
         $this->mySQLRepositoryTest->isCheckSum();
         self::assertTrue(true);
     }
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index 1bb43b88..72d22984 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -1,4 +1,7 @@
-<?php
-include __DIR__ . '/../vendor/autoload.php';
-
-date_default_timezone_set('UTC');
\ No newline at end of file
+<?php
+
+declare(strict_types=1);
+
+include __DIR__ . '/../vendor/autoload.php';
+
+date_default_timezone_set('UTC');