diff --git a/docker-compose.yml b/docker-compose.yml
index 7c2627c6a177b5b32b8a0a3867b7d28955e37d3f..4de6d88723015ce6d96923f73a7b4098c803faeb 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -34,20 +34,6 @@ services:
       retries: 3
     ports:
       - "5432:5432"
-
-  flyway:
-    image: flyway/flyway:9-alpine
-    command: -connectRetries=60 -baselineVersion="0.0" baseline migrate info
-    volumes:
-      - ./flyway/sql:/flyway/sql:ro
-    environment:
-      - FLYWAY_URL=${JDBC_URL}
-      - FLYWAY_USER=${DATABASE_USERNAME}
-      - FLYWAY_PASSWORD=${DATABASE_PASSWORD}
-    depends_on:
-      - db
-    networks:
-      - db
 networks:
   db:
 
diff --git a/pom.xml b/pom.xml
index e105af5a7626687e88431591c776e283ca4d8e8f..49dae4a37672d5042cddc158db3b6f5fa551d7ba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,6 +23,10 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-jpa</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-hateoas</artifactId>
diff --git a/src/main/java/com/takima/entrainement/toDo/controllers/ToDoApi.java b/src/main/java/com/takima/entrainement/toDo/controllers/ToDoApi.java
new file mode 100644
index 0000000000000000000000000000000000000000..a358df5e57a66764bbe98f4c30ff3fdfc5c7ae80
--- /dev/null
+++ b/src/main/java/com/takima/entrainement/toDo/controllers/ToDoApi.java
@@ -0,0 +1,99 @@
+package com.takima.entrainement.toDo.controllers;
+
+
+import com.takima.entrainement.core.pagination.PageSearch;
+import com.takima.entrainement.core.pagination.SearchSpecification;
+import com.takima.entrainement.toDo.models.ToDo;
+import com.takima.entrainement.toDo.services.ToDoService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.web.SortDefault;
+import org.springframework.hateoas.EntityModel;
+import org.springframework.hateoas.server.RepresentationModelAssembler;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.NoSuchElementException;
+
+import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
+import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
+
+@RestController
+@RequestMapping("/api/todos")
+public class ToDoApi {
+
+    private final ToDoService todoService;
+    private final ToDoResourceAssembler assembler;
+
+    @Autowired
+    ToDoApi(ToDoService todoService, ToDoResourceAssembler assembler) {
+        this.todoService = todoService;
+        this.assembler = assembler;
+    }
+
+    @GetMapping
+    public Page<ToDo> getAll(@RequestParam(required = false, defaultValue = "") String search,
+                             @SortDefault Sort sort,
+                             @RequestParam(required = false, defaultValue = "20") int limit,
+                             @RequestParam(required = false, defaultValue = "0") int offset) {
+
+        PageSearch<ToDo> pageSearch = PageSearch.<ToDo>builder()
+                .sort(sort)
+                .search(SearchSpecification.parse(search))
+                .limit(limit)
+                .offset(offset)
+                .build();
+
+        return todoService.findAll(pageSearch);
+    }
+
+    @GetMapping(value = "{todoId}")
+    public ToDo getOne(@PathVariable long todoId) {
+        ToDo todo = todoService.findById(todoId)
+                .orElseThrow(() -> new NoSuchElementException(String.format("no todo with id %d", todoId)));
+        return todo;
+    }
+
+    @PostMapping
+    public ResponseEntity<EntityModel<ToDo>> create(@RequestBody ToDo todo) {
+        if (todo.getId() != null) {
+            throw new IllegalArgumentException("cannot create a todo and specify the ID");
+        }
+
+        todo = todoService.create(todo);
+
+        return new ResponseEntity<>(assembler.toModel(todo), HttpStatus.CREATED);
+    }
+
+    @PutMapping("/{todoId}")
+    public ResponseEntity<EntityModel<ToDo>> update(@RequestBody ToDo todo, @PathVariable long todoId) {
+
+//        ToDo todoToUpdate = todoService.findById(todoId)
+//                .orElseThrow(() -> new NoSuchElementException(String.format("no todo with id %d", todoId)));
+
+        todo.setId(todoId);
+        ToDo todoToUpdate=todoService.update(todo);
+
+        return new ResponseEntity<>(assembler.toModel(todoToUpdate), HttpStatus.OK);
+    }
+
+    @DeleteMapping(value = "{todoId}")
+    public void delete(@PathVariable long todoId) {
+        todoService.deleteById(todoId);
+    }
+
+}
+
+@Component
+class ToDoResourceAssembler implements RepresentationModelAssembler<ToDo, EntityModel<ToDo>> {
+
+    public EntityModel<ToDo> toModel(ToDo todo) {
+
+        return EntityModel.of(todo,
+                linkTo(methodOn(ToDoApi.class).getOne(todo.getId())).withSelfRel());
+    }
+
+}
diff --git a/src/main/java/com/takima/entrainement/toDo/daos/DataJpaToDoDao.java b/src/main/java/com/takima/entrainement/toDo/daos/DataJpaToDoDao.java
new file mode 100644
index 0000000000000000000000000000000000000000..81ec1f9d04d42ca683d6effe1b491f50d314ec4c
--- /dev/null
+++ b/src/main/java/com/takima/entrainement/toDo/daos/DataJpaToDoDao.java
@@ -0,0 +1,19 @@
+package com.takima.entrainement.toDo.daos;
+
+import com.takima.entrainement.core.pagination.PageSearch;
+import com.takima.entrainement.toDo.models.ToDo;
+import org.springframework.data.domain.Page;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+
+public interface DataJpaToDoDao extends ToDoDao, JpaRepository<ToDo, Long>, JpaSpecificationExecutor<ToDo> {
+
+    default Page<ToDo> findAll(PageSearch<ToDo> page) {
+        return findAll(page.getSearch(), page);
+    }
+
+    default long count(PageSearch page) {
+        return count(page.getSearch());
+    }
+}
diff --git a/src/main/java/com/takima/entrainement/toDo/daos/ToDoDao.java b/src/main/java/com/takima/entrainement/toDo/daos/ToDoDao.java
new file mode 100644
index 0000000000000000000000000000000000000000..48d2dadc95377c7fc90d26cd1f3dbdcadbafc340
--- /dev/null
+++ b/src/main/java/com/takima/entrainement/toDo/daos/ToDoDao.java
@@ -0,0 +1,18 @@
+package com.takima.entrainement.toDo.daos;
+
+
+import com.takima.entrainement.core.pagination.PageSearch;
+import com.takima.entrainement.toDo.models.ToDo;
+import org.springframework.data.domain.Page;
+
+import java.util.Optional;
+
+public interface ToDoDao {
+    Page<ToDo> findAll(PageSearch<ToDo> pageSearch);
+
+    Optional<ToDo> findById(Long id);
+
+    ToDo save(ToDo group);
+
+    void deleteById(Long id);
+}
diff --git a/src/main/java/com/takima/entrainement/toDo/models/State.java b/src/main/java/com/takima/entrainement/toDo/models/State.java
new file mode 100644
index 0000000000000000000000000000000000000000..5689d63a6d930e1815db72ebea40a24b148c05ef
--- /dev/null
+++ b/src/main/java/com/takima/entrainement/toDo/models/State.java
@@ -0,0 +1,49 @@
+package com.takima.entrainement.toDo.models;
+
+import jakarta.persistence.AttributeConverter;
+import jakarta.persistence.Converter;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+public enum State {
+    TODO("todo"),
+    DONE("done");
+
+    public static final Map<String, State> lut = new HashMap<>();
+    private final String code;
+
+    State(String code) {
+        this.code = code;
+    }
+
+    public static State fromCode(String code) {
+        return lut.computeIfAbsent(code, (s) -> Arrays.stream(values())
+                .filter(c -> c.code.equals(code))
+                .findAny()
+                .orElseThrow(() -> new IllegalArgumentException("no currency found for code " + code))
+
+        );
+    }
+
+    @Converter(autoApply = true)
+    public static class StateConverter
+            implements AttributeConverter<State, String> {
+
+        public String convertToDatabaseColumn(State state) {
+            if (state == null) {
+                return null;
+            }
+
+            return state.code;
+        }
+
+        public State convertToEntityAttribute(String dbColumn) {
+            if (dbColumn == null) {
+                return null;
+            }
+            return State.fromCode(dbColumn);
+        }
+    }
+}
diff --git a/src/main/java/com/takima/entrainement/toDo/models/ToDo.java b/src/main/java/com/takima/entrainement/toDo/models/ToDo.java
new file mode 100644
index 0000000000000000000000000000000000000000..0f1456938c456b755669fa3cc872a4f6fee36f5f
--- /dev/null
+++ b/src/main/java/com/takima/entrainement/toDo/models/ToDo.java
@@ -0,0 +1,29 @@
+package com.takima.entrainement.toDo.models;
+
+import jakarta.persistence.*;
+import jakarta.validation.constraints.NotBlank;
+import lombok.*;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@Entity
+public class ToDo {
+    @Id
+    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "to_do_id_seq")
+    private Long id;
+
+    @Column
+    @NotBlank
+    private String name;
+
+    @Column
+    private String description;
+
+    @Column
+    @Convert(converter = State.StateConverter.class)
+    private State state = State.TODO;
+
+}
diff --git a/src/main/java/com/takima/entrainement/toDo/services/ToDoService.java b/src/main/java/com/takima/entrainement/toDo/services/ToDoService.java
new file mode 100644
index 0000000000000000000000000000000000000000..c6acb2e24fcadbf5f4440038abdb221eac914cac
--- /dev/null
+++ b/src/main/java/com/takima/entrainement/toDo/services/ToDoService.java
@@ -0,0 +1,21 @@
+package com.takima.entrainement.toDo.services;
+
+
+
+import com.takima.entrainement.core.pagination.PageSearch;
+import com.takima.entrainement.toDo.models.ToDo;
+import org.springframework.data.domain.Page;
+
+import java.util.Optional;
+
+public interface ToDoService {
+    void deleteById(long toDoId);
+
+    Page<ToDo> findAll(PageSearch<ToDo> pageSearch);
+
+    Optional<ToDo> findById(long id);
+
+    ToDo create(ToDo toDo);
+
+    ToDo update(ToDo toDo);
+}
diff --git a/src/main/java/com/takima/entrainement/toDo/services/impl/ToDoServiceImpl.java b/src/main/java/com/takima/entrainement/toDo/services/impl/ToDoServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..c739e38fa6f6be95e4705553c94758cdbdf35f85
--- /dev/null
+++ b/src/main/java/com/takima/entrainement/toDo/services/impl/ToDoServiceImpl.java
@@ -0,0 +1,46 @@
+package com.takima.entrainement.toDo.services.impl;
+
+
+import com.takima.entrainement.core.pagination.PageSearch;
+import com.takima.entrainement.toDo.daos.ToDoDao;
+import com.takima.entrainement.toDo.models.ToDo;
+import com.takima.entrainement.toDo.services.ToDoService;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Optional;
+
+@Service
+@AllArgsConstructor
+public class ToDoServiceImpl implements ToDoService {
+
+    private final ToDoDao toDoDao;
+
+    @Override
+    public void deleteById(long toDoId) {
+        toDoDao.deleteById(toDoId);
+    }
+
+    @Override
+    public ToDo create(ToDo toDo) {
+        return toDoDao.save(toDo);
+    }
+
+    @Override
+    public ToDo update(ToDo toDo) {
+        return toDoDao.save(toDo);
+    }
+
+    @Override
+    public Page<ToDo> findAll(PageSearch<ToDo> pageSearch) {
+        return toDoDao.findAll(pageSearch);
+    }
+
+    @Override
+    public Optional<ToDo> findById(long id) {
+        return toDoDao.findById(id);
+    }
+
+}
diff --git a/src/main/resources/db/migration/V1_0__list_and_todo_tables.sql b/src/main/resources/db/migration/V1_0__list_and_todo_tables.sql
new file mode 100644
index 0000000000000000000000000000000000000000..1e0984b63704acfa33ab850483ef2cacd774429d
--- /dev/null
+++ b/src/main/resources/db/migration/V1_0__list_and_todo_tables.sql
@@ -0,0 +1,10 @@
+CREATE TABLE public.to_do
+(
+    id          BIGSERIAL PRIMARY KEY,
+    name        VARCHAR   NOT NULL,
+    description VARCHAR,
+    state     VARCHAR  NOT NULL CHECK (state IN ('todo', 'done'))
+);
+
+
+ALTER SEQUENCE to_do_id_seq RESTART 100 INCREMENT BY 50;
\ No newline at end of file
diff --git a/src/main/resources/db/migration/V1_0__user_and_list_and_todo_tables.sql b/src/main/resources/db/migration/V1_0__user_and_list_and_todo_tables.sql
deleted file mode 100644
index f8392af77c3a7b75ed43af93ca32037f4d7fcbc8..0000000000000000000000000000000000000000
--- a/src/main/resources/db/migration/V1_0__user_and_list_and_todo_tables.sql
+++ /dev/null
@@ -1,21 +0,0 @@
-CREATE TABLE public.userb
-(
-    id      BIGSERIAL PRIMARY KEY,
-    name    VARCHAR NOT NULL
-);
-
-CREATE TABLE public.todo_list
-(
-    id      BIGSERIAL PRIMARY KEY,
-    name    VARCHAR NOT NULL,
-    user_id BIGINT  NOT NULL REFERENCES userb (id)
-);
-
-CREATE TABLE public.todo
-(
-    id          BIGSERIAL PRIMARY KEY,
-    name        VARCHAR   NOT NULL,
-    description VARCHAR,
-    checked     BOOLEAN   NOT NULL DEFAULT FALSE,
-    seller_id   BIGINT    NOT NULL REFERENCES todo_list (id)
-);
diff --git a/src/test/java/com/takima/entrainement/toDo/controllers/ToDoApiIT.java b/src/test/java/com/takima/entrainement/toDo/controllers/ToDoApiIT.java
new file mode 100644
index 0000000000000000000000000000000000000000..f14a517f2f445f714c3684108f8db9bb6ea2d828
--- /dev/null
+++ b/src/test/java/com/takima/entrainement/toDo/controllers/ToDoApiIT.java
@@ -0,0 +1,166 @@
+package com.takima.entrainement.toDo.controllers;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.takima.entrainement.toDo.models.State;
+import com.takima.entrainement.toDo.models.ToDo;
+import com.takima.entrainement.toDo.services.ToDoService;
+import org.flywaydb.core.Flyway;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.test.web.servlet.MockMvc;
+
+import java.util.Optional;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@SpringBootTest
+@ExtendWith(SpringExtension.class)
+@AutoConfigureMockMvc
+@ActiveProfiles("test")
+public class ToDoApiIT {
+
+    static Flyway flyway;
+    final int TODO_ID = 1;
+    final String TODO_NAME = "Tache";
+    final String URL = "/api/todos";
+    @Autowired
+    ToDoService toDoService;
+    @Autowired
+    private MockMvc mvc;
+
+    @Autowired
+    ToDoApiIT(Flyway flyway) {
+        ToDoApiIT.flyway = flyway;
+    }
+
+    @Nested
+    @DisplayName("1-GET a todo/")
+    class GetDiscounts {
+        @AfterAll
+        static void clearDatabase() {
+            flyway.clean();
+            flyway.migrate();
+        }
+
+        @Nested
+        @DisplayName("with a valid todo id")
+        class WithValidCustomerId {
+            final String testURL = URL + "/" + TODO_ID;
+
+            @Test
+            @DisplayName("should give status 200 OK")
+            void shouldGive200() throws Exception {
+                mvc.perform(get(testURL)
+                                .contentType(MediaType.APPLICATION_JSON_VALUE))
+                        .andExpect(status().isOk())
+                        .andExpect(jsonPath("$['name']", is(TODO_NAME)));
+
+            }
+        }
+    }
+
+
+    @Nested
+    @DisplayName("2-POST add todo")
+    class AddDiscounts {
+
+        @AfterAll
+        static void clearDatabase() {
+            flyway.clean();
+            flyway.migrate();
+        }
+
+        @Nested
+        @DisplayName("with a valid todo ")
+        class WithValidToDO {
+            @Test
+            @DisplayName("should give status 201 Created")
+            void shouldGive200() throws Exception {
+
+                ToDo todo = ToDo.builder().name("Tache").state(State.TODO).build();
+
+                String jsonToDO = new ObjectMapper().writeValueAsString(todo);
+                mvc.perform(post(URL)
+                                .content(jsonToDO)
+                                .contentType(MediaType.APPLICATION_JSON_VALUE))
+                        .andExpect(status().isCreated());
+
+            }
+        }
+    }
+
+    @Nested
+    @DisplayName("3-PUT update todo")
+    class UpdateTodo {
+
+        @AfterAll
+        static void clearDatabase() {
+            flyway.clean();
+            flyway.migrate();
+        }
+
+        @Nested
+        @DisplayName("with a valid todo ")
+        class WithValidToDO {
+            @Test
+            @DisplayName("should update the todo")
+            void shouldGive200() throws Exception {
+
+                String newName = "Tache2";
+                ToDo todo = ToDo.builder().name(newName).state(State.TODO).build();
+
+                String jsonToDO = new ObjectMapper().writeValueAsString(todo);
+
+                mvc.perform(put(URL+"/" + TODO_ID)
+                                .content(jsonToDO)
+                                .contentType(MediaType.APPLICATION_JSON_VALUE));
+
+                assertEquals(toDoService.findById(TODO_ID).get().getName(), newName);
+            }
+        }
+    }
+
+    @Nested
+    @DisplayName("4-DELETE remove todo/")
+    class RemoveDiscount {
+
+        @AfterAll
+        static void clearDatabase() {
+            flyway.clean();
+            flyway.migrate();
+        }
+
+
+        @Nested
+        @DisplayName("with an existing id")
+        class WithValidId {
+
+
+            @Test
+            @DisplayName("should give status 200 OK remove the todo")
+            void shouldGive200() throws Exception {
+                mvc.perform(delete(URL + "/" + TODO_ID)
+                                .contentType(MediaType.APPLICATION_JSON_VALUE))
+                        .andExpect(status().isOk());
+                Optional<ToDo> todo = toDoService.findById(TODO_ID);
+                assertEquals(todo, Optional.empty());
+            }
+
+        }
+    }
+}
+
+
diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties
new file mode 100644
index 0000000000000000000000000000000000000000..495591b2a139bb8195a3d2cdcd339b995045edec
--- /dev/null
+++ b/src/test/resources/application-test.properties
@@ -0,0 +1,12 @@
+spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver
+spring.datasource.url=jdbc:tc:postgresql:15.2://localhost:5432/entrainement_db
+spring.datasource.password=${DATABASE_PASSWORD:ilovethierion}
+spring.datasource.username=${DATABASE_USERNAME:ilovethierion}
+logging.level.root=info
+logging.level.org.hibernate.SQL=DEBUG
+logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
+spring.flyway.enabled=true
+spring.flyway.baseline-version=0
+spring.flyway.locations=filesystem:src/main/resources/db/migration, filesystem:src/test/resources/db
+spring.flyway.cleanDisabled=false
+
diff --git a/src/test/resources/db/V9999__insert_data.sql b/src/test/resources/db/V9999__insert_data.sql
new file mode 100644
index 0000000000000000000000000000000000000000..206c03f0e2738fc5437a653a72765cd14deb4779
--- /dev/null
+++ b/src/test/resources/db/V9999__insert_data.sql
@@ -0,0 +1,3 @@
+INSERT INTO to_do (id, name, description, state)
+VALUES (1, 'Tache', 'Description de la tâche 1', 'todo');
+