diff --git a/app/classes/Framadate/FramaDB.php b/app/classes/Framadate/FramaDB.php
index fe6783359d87585b2d0fa7d4ae5cce24cd9dcfee..b93ea095a4750bcef961ae74fdf115db3bedb570 100644
--- a/app/classes/Framadate/FramaDB.php
+++ b/app/classes/Framadate/FramaDB.php
@@ -30,11 +30,23 @@ class FramaDB {
         $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
     }
 
-    function areTablesCreated() {
+    /**
+     * @return \PDO Connection to database
+     */
+    function getPDO() {
+        return $this->pdo;
+    }
+
+    /**
+     * Find all tables in database.
+     *
+     * @return array The array of table names
+     */
+    function allTables() {
         $result = $this->pdo->query('SHOW TABLES');
         $schemas = $result->fetchAll(\PDO::FETCH_COLUMN);
 
-        return 0 != count(array_diff($schemas, ['comment', 'poll', 'slot', 'vote']));
+        return $schemas;
     }
 
     function prepare($sql) {
diff --git a/app/classes/Framadate/Migration/From_0_8_to_0_9_Migration.php b/app/classes/Framadate/Migration/From_0_8_to_0_9_Migration.php
new file mode 100644
index 0000000000000000000000000000000000000000..5e0d452a06f6107243b746b282d3b498c31661cf
--- /dev/null
+++ b/app/classes/Framadate/Migration/From_0_8_to_0_9_Migration.php
@@ -0,0 +1,144 @@
+<?php
+namespace Framadate\Migration;
+
+/**
+ * This class executes the aciton in database to migrate data from version 0.8 to 0.9.
+ *
+ * @package Framadate\Migration
+ */
+class From_0_8_to_0_9_Migration implements Migration {
+
+    function __construct() {
+    }
+
+    function execute(\PDO $pdo) {
+        $this->createPollTable($pdo);
+        $this->migrateFromSondageToPoll($pdo);
+
+        $this->createSlotTable($pdo);
+        $this->migrateFromSujetStudsToSlot($pdo);
+
+        $this->createCommentTable($pdo);
+        $this->migrateFromCommentsToComment($pdo);
+
+        $this->createVoteTable($pdo);
+        $this->migrateFromUserStudsToVote($pdo);
+
+        return true;
+    }
+
+    private function createPollTable(\PDO $pdo) {
+        $pdo->exec('
+CREATE TABLE IF NOT EXISTS `poll` (
+  `id`              CHAR(16)  NOT NULL,
+  `admin_id`        CHAR(24)  NOT NULL,
+  `title`           TEXT      NOT NULL,
+  `description`     TEXT,
+  `admin_name`      VARCHAR(64) DEFAULT NULL,
+  `admin_mail`      VARCHAR(128) DEFAULT NULL,
+  `creation_date`   TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `end_date`        TIMESTAMP NOT NULL DEFAULT \'0000-00-00 00:00:00\',
+  `format`          VARCHAR(1) DEFAULT NULL,
+  `editable`        TINYINT(1) DEFAULT \'0\',
+  `receiveNewVotes` TINYINT(1) DEFAULT \'0\',
+  `active`          TINYINT(1) DEFAULT \'1\',
+  PRIMARY KEY (`id`)
+)
+  ENGINE = InnoDB
+  DEFAULT CHARSET = utf8');
+    }
+
+    private function migrateFromSondageToPoll(\PDO $pdo) {
+        $pdo->exec('
+INSERT INTO `poll`
+(`id`, `admin_id`, `title`, `description`, `admin_name`, `admin_mail`, `creation_date`, `end_date`, `format`, `editable`, `receiveNewVotes`, `active`)
+  SELECT
+    `id_sondage`,
+    `id_sondage_admin`,
+    `titre`,
+    `commentaires`,
+    `nom_admin`,
+    `mail_admin`,
+    `date_creation`,
+    `date_fin`,
+    SUBSTR(`format`, 1, 1) AS `format`,
+    CASE SUBSTR(`format`, 2, 1)
+    WHEN \'+\' THEN 1
+    ELSE 0 END             AS `editable`,
+    `mailsonde`,
+    CASE SUBSTR(`format`, 2, 1)
+    WHEN \'-\' THEN 0
+    ELSE 1 END             AS `active`
+  FROM sondage');
+    }
+
+    private function createSlotTable(\PDO $pdo) {
+        $pdo->exec('
+CREATE TABLE IF NOT EXISTS `slot` (
+  `id`      INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+  `poll_id` CHAR(16)         NOT NULL,
+  `title`   TEXT,
+  `moments` TEXT,
+  PRIMARY KEY (`id`),
+  KEY `poll_id` (`poll_id`)
+)
+  ENGINE = InnoDB
+  DEFAULT CHARSET = utf8');
+    }
+
+    private function migrateFromSujetStudsToSlot(\PDO $pdo) {
+        // TODO Implements
+    }
+
+    private function createCommentTable(\PDO $pdo) {
+        $pdo->exec('
+CREATE TABLE IF NOT EXISTS `comment` (
+  `id`      INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+  `poll_id` CHAR(16)         NOT NULL,
+  `name`    TEXT,
+  `comment` TEXT             NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `poll_id` (`poll_id`)
+)
+  ENGINE = InnoDB
+  DEFAULT CHARSET = utf8');
+    }
+
+    private function migrateFromCommentsToComment(\PDO $pdo) {
+        $pdo->exec('
+INSERT INTO `comment`
+(`poll_id`, `name`, `comment`)
+  SELECT
+    `id_sondage`,
+    `usercomment`,
+    `comment`
+  FROM `comments`');
+    }
+
+    private function createVoteTable(\PDO $pdo) {
+        $pdo->exec('
+CREATE TABLE IF NOT EXISTS `vote` (
+  `id`      INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+  `poll_id` CHAR(16)         NOT NULL,
+  `name`    VARCHAR(64)      NOT NULL,
+  `choices` TEXT             NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `poll_id` (`poll_id`)
+)
+  ENGINE = InnoDB
+  DEFAULT CHARSET = utf8');
+    }
+
+    private function migrateFromUserStudsToVote(\PDO $pdo) {
+        $pdo->exec('
+INSERT INTO `vote`
+(`poll_id`, `name`, `choices`)
+  SELECT
+    `id_sondage`,
+    `nom`,
+    REPLACE(REPLACE(REPLACE(`reponses`, 1, \'X\'), 2, 1), \'X\', 2)
+  FROM `user_studs`');
+    }
+
+}
+ 
\ No newline at end of file
diff --git a/app/classes/Framadate/Migration/Migration.php b/app/classes/Framadate/Migration/Migration.php
new file mode 100644
index 0000000000000000000000000000000000000000..e6d0eb9e5648f748dcef35f32b7114f30f83eb92
--- /dev/null
+++ b/app/classes/Framadate/Migration/Migration.php
@@ -0,0 +1,15 @@
+<?php
+namespace Framadate\Migration;
+
+interface Migration {
+
+    /**
+     * This methode is called only one time in the migration page.
+     *
+     * @param \PDO $pdo The connection to database
+     * @return bool true is the execution succeeded
+     */
+    function execute(\PDO $pdo);
+
+}
+ 
\ No newline at end of file
diff --git a/app/inc/constants.php.template b/app/inc/constants.php.template
index d7b58bca6156fca6322de0b5c41401804c8bee6e..61781a6c40b177bbec0510244b1847cdbdada38e 100644
--- a/app/inc/constants.php.template
+++ b/app/inc/constants.php.template
@@ -41,6 +41,9 @@ const DB_PASSWORD = '<database password>';
 // Database server name, leave empty to use a socket
 const DB_CONNECTION_STRING = 'mysql:host=<database host>;dbname=<database name>;port=<database port>';
 
+// Name of the table that store migration script already executed
+const MIGRATION_TABLE = 'framadate_migration';
+
 // Default Language using POSIX variant of BC P47 standard (choose in $ALLOWED_LANGUAGES)
 const LANGUE = 'fr_FR';
 
diff --git a/bandeaux.php b/bandeaux.php
index f93117796d12572709a51c608f1e4c1467746516..a5aea4fc3d26ebb69665e5b3b325c1438cd1ed40 100644
--- a/bandeaux.php
+++ b/bandeaux.php
@@ -45,7 +45,9 @@ function bandeau_titre($titre)
     <main role="main">';
     
     global $connect;
-    if ($connect->areTablesCreated()) {
+    $tables = $connect->allTables();
+    $diff = array_diff($tables, ['comment', 'poll', 'slot', 'vote']);
+    if (0 != count($diff)) {
         echo '<div class="alert alert-danger">'. _('Framadate is not properly installed, please check the "INSTALL" to setup the database before continuing.') .'</div>';
         bandeau_pied();
         die();
diff --git a/from_0-8_to_0-9.sql b/from_0-8_to_0-9.sql
deleted file mode 100644
index 2b2ae23db08b0816f8ed19310ea09af4ac5b0e7a..0000000000000000000000000000000000000000
--- a/from_0-8_to_0-9.sql
+++ /dev/null
@@ -1,136 +0,0 @@
--- --------------------------------------------------------
-
---
--- Table structure `poll`
---
-
-CREATE TABLE IF NOT EXISTS `poll` (
-  `id`              CHAR(16)  NOT NULL,
-  `admin_id`        CHAR(24)  NOT NULL,
-  `title`           TEXT      NOT NULL,
-  `description`     TEXT,
-  `admin_name`      VARCHAR(64) DEFAULT NULL,
-  `admin_mail`      VARCHAR(128) DEFAULT NULL,
-  `creation_date`   TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-  `end_date`        TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
-  `format`          VARCHAR(1) DEFAULT NULL,
-  `editable`        TINYINT(1) DEFAULT '0',
-  `receiveNewVotes` TINYINT(1) DEFAULT '0',
-  `active`          TINYINT(1) DEFAULT '1',
-  PRIMARY KEY (`id`)
-)
-  ENGINE = InnoDB
-  DEFAULT CHARSET = utf8;
-
--- --------------------------------------------------------
-
---
--- Table structure `slot`
---
-
-CREATE TABLE IF NOT EXISTS `slot` (
-  `id`      INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
-  `poll_id` CHAR(16)         NOT NULL,
-  `title`   TEXT,
-  `moments` TEXT,
-  PRIMARY KEY (`id`),
-  KEY `poll_id` (`poll_id`)
-)
-  ENGINE = InnoDB
-  DEFAULT CHARSET = utf8;
-
--- --------------------------------------------------------
-
---
--- Table structure `comment`
---
-
-CREATE TABLE IF NOT EXISTS `comment` (
-  `id`      INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
-  `poll_id` CHAR(16)         NOT NULL,
-  `name`    TEXT,
-  `comment` TEXT             NOT NULL,
-  PRIMARY KEY (`id`),
-  KEY `poll_id` (`poll_id`)
-)
-  ENGINE = InnoDB
-  DEFAULT CHARSET = utf8;
-
--- --------------------------------------------------------
-
---
--- Table structure `vote`
---
-
-CREATE TABLE IF NOT EXISTS `vote` (
-  `id`      INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
-  `poll_id` CHAR(16)         NOT NULL,
-  `name`    VARCHAR(64)      NOT NULL,
-  `choices` TEXT             NOT NULL,
-  PRIMARY KEY (`id`),
-  KEY `poll_id` (`poll_id`)
-)
-  ENGINE = InnoDB
-  DEFAULT CHARSET = utf8;
-
--- --------------------------------------------------------
-
---
--- Migrate data from `sondage` to `poll`
---
-
-INSERT INTO `poll`
-(`id`, `admin_id`, `title`, `description`, `admin_name`, `admin_mail`, `creation_date`, `end_date`, `format`, `editable`, `receiveNewVotes`, `active`)
-  SELECT
-    `id_sondage`,
-    `id_sondage_admin`,
-    `titre`,
-    `commentaires`,
-    `nom_admin`,
-    `mail_admin`,
-    `titre`,
-    `date_creation`,
-    `date_fin`,
-    SUBSTR(`format`, 1, 1) AS `format`,
-    CASE SUBSTR(`format`, 2, 1)
-    WHEN '+' THEN 1
-    ELSE 0 END             AS `editable`,
-    `mailsonde`,
-    CASE SUBSTR(`format`, 2, 1)
-    WHEN '-' THEN 0
-    ELSE 1 END             AS `active`
-  FROM sondage;
-
--- --------------------------------------------------------
-
---
--- Migrate data from `sujet_studs` to `slot`
---
-
--- TODO Migrate this, is not so simple
-/*INSERT INTO `slot`
-(`poll_id`, `title`, `moments`)
-    SELECT `id_sondage`,
-      FROM `user_studs`;*/
-
--- --------------------------------------------------------
-
---
--- Migrate data from `comments` to `comment`
---
-
-INSERT INTO `comment`
-(`poll_id`, `name`, `comment`)
-  SELECT `id_sondage`, `usercomment`, `comment`
-  FROM `comments`;
-
--- --------------------------------------------------------
-
---
--- Migrate data from `user_studs` to `vote`
---
-
-INSERT INTO `vote`
-(`poll_id`, `name`, `choices`)
-  SELECT `id_sondage`, `nom`, REPLACE(REPLACE(REPLACE(`reponses`, '1', 'X'), '2', '1'), 'X', 2)
-  FROM `user_studs`;
diff --git a/migration.php b/migration.php
new file mode 100644
index 0000000000000000000000000000000000000000..af64ef9311508f99fce93144068486fb03d4a707
--- /dev/null
+++ b/migration.php
@@ -0,0 +1,60 @@
+<?php
+use Framadate\Migration\From_0_8_to_0_9_Migration;
+use Framadate\Migration\Migration;
+use Framadate\Utils;
+
+include_once __DIR__ . '/app/inc/init.php';
+
+function output($msg) {
+    echo $msg . '<br/>';
+}
+
+// List a Migration sub classes to execute
+$migrations = [
+    new From_0_8_to_0_9_Migration(),
+    new From_0_8_to_0_9_Migration()
+];
+
+// Check if MIGRATION_TABLE already exists
+$tables = $connect->allTables();
+$pdo = $connect->getPDO();
+
+if (!in_array(MIGRATION_TABLE, $tables)) {
+    $pdo->exec('
+CREATE TABLE IF NOT EXISTS `' . MIGRATION_TABLE . '` (
+  `id`   INT(11)  UNSIGNED NOT NULL AUTO_INCREMENT,
+  `name` TEXT              NOT NULL,
+  `execute_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`)
+)
+  ENGINE = MyISAM
+  DEFAULT CHARSET = utf8;');
+
+    output('Table ' . MIGRATION_TABLE . ' created.');
+}
+
+$selectStmt = $pdo->prepare('SELECT id FROM ' . MIGRATION_TABLE . ' WHERE name=?');
+$insertStmt = $pdo->prepare('INSERT INTO ' . MIGRATION_TABLE . ' (name) VALUES (?)');
+
+// Loop on every Migration sub classes
+foreach ($migrations as $migration) {
+    $className = get_class($migration);
+
+    // Check if $className is a Migration sub class
+    if (!$migration instanceof Migration) {
+        output('The class '. $className . ' is not a sub class of Framadate\\Migration\\Migration.');
+        exit;
+    }
+
+    // Check if the Migration is already executed
+    $selectStmt->execute([$className]);
+    $executed = $selectStmt->rowCount();
+    $selectStmt->closeCursor();
+
+    if (!$executed) {
+        $migration->execute($pdo);
+        $insertStmt->execute([$className]);
+        output('Migration done: ' . $className);
+    }
+
+}