Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Unbreakable REST APIs: desenhando e implementan...

Unbreakable REST APIs: desenhando e implementando APIs robustas e previsíveis com fail fast e idempotency

(🎥 RECORDING: https://youtu.be/_MYy68tKsbA?list=PLHMMERsvy9EyWQPru4SrJAYHEGKfkjRgP&t=8954)

Existe uma grande diferença ao desenhar sistemas distribuídos e sistemas centralizados. Estar ciente delas é o que te permite construir sistemas escaláveis e resilientes.

Nesta talk você vai entender alguns desafios comuns e escorregadios ao construir sistemas distribuídos nesse mundo de microsserviços. Mas não se engane, até mesmo aquele seu CRUD simples com poucas chamadas a um banco de dados relacional pode ser uma fonte de incertezas e problemas de concorrência e consistência de dados.

Para lidar com estes percalços, você aprenderá como construir APIs REST robustas e previsíveis através de técnicas de resiliência como fail fast e idempotência. Apesar de ambas serem fáceis de entender, elas não necessariamente são simples de implementar. Para isso, tiraremos proveito do poder das transações e propriedades ACID dos bancos relacionais que nos ajudarão a minimizar vários desafios de concorrência e de cenários de indisponibilidade e inconsistência de dados.

Se você acha que uma API REST de CRUD é algo simples, então essa talk é para você!

Rafael Ponte

August 03, 2024
Tweet

More Decks by Rafael Ponte

Other Decks in Technology

Transcript

  1. UNBREAKABLE REST APIs Desenhando e implementando APIs robustas e previsíveis

    com fail fast e idempotency Rafael Ponte | @rponte
  2. https://www.youtube.com/watch?v=GBTdnfD6s5Q “ Eu não escrevi um livro sobre microservices, mas

    sim sobre como você arquiteta para Entrega Contínua. ” — Sam Newman
  3. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  4. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  5. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  6. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  7. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  8. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  9. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  10. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  11. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  12. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } } Injetado pelo Spring!
  13. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  14. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  15. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  16. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  17. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  18. @rponte Rafael Ponte Brazilian Software Engineer at aka Prince of

    the Ocean aka Maharaja of the Legacies 19+ years of experience working with Java and distributed systems Loves working with "boring techs"
  19. ?

  20. ?

  21. UNBREAKABLE REST APIs Desenhando e implementando APIs robustas e previsíveis

    com fail fast e idempotency Rafael Ponte | @rponte
  22. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  23. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  24. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  25. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  26. @Repository public interface UserRepository extends JpaRepository<User, Long> { @QueryHints({ @QueryHint(name

    = "javax.persistence.query.timeout", value = "2000") }) @Override public Optional<User> findById(Long id); }
  27. @Repository public interface UserRepository extends JpaRepository<User, Long> { @QueryHints({ @QueryHint(name

    = "javax.persistence.query.timeout", value = "2000") }) @Override public Optional<User> findById(Long id); }
  28. @Repository public interface UserRepository extends JpaRepository<User, Long> { @QueryHints({ @QueryHint(name

    = "javax.persistence.query.timeout", value = "2000") }) @Override public Optional<User> findById(Long id); }
  29. @Repository public interface UserRepository extends JpaRepository<User, Long> { @QueryHints({ @QueryHint(name

    = "javax.persistence.query.timeout", value = "2000") }) @Override public Optional<User> findById(Long id); }
  30. @Repository public interface UserRepository extends JpaRepository<User, Long> { @QueryHints({ @QueryHint(name

    = "javax.persistence.query.timeout", value = "2000") }) @Override public Optional<User> findById(Long id); }
  31. @Repository public interface UserRepository extends JpaRepository<User, Long> { @QueryHints({ @QueryHint(name

    = "javax.persistence.query.timeout", value = "2000") }) @Override public Optional<User> findById(Long id); }
  32. @Repository public interface UserRepository extends JpaRepository<User, Long> { @QueryHints({ @QueryHint(name

    = "javax.persistence.query.timeout", value = "2000") }) @Override public Optional<User> findById(Long id); }
  33. SELECT u.* FROM user u WHERE u.id = 42 --

    SQL Error: 0, SQLState: 57014 -- ERROR: canceling statement due to user request
  34. @RestController public class FindUserController { @Autowired private UserRepository repository; @GetMapping("/api/users/{id}")

    public ResponseEntity<?> findById(@PathVariable Long id) { User user = repository.findById(id).orElseThrow(() -> { return new ResponseStatusException(NOT_FOUND, "User not found"); }); return ResponseEntity .ok(new UserResponse(user)); } }
  35. Aplicação Usuário Banco de dados GET /api/users/42 HTTP 503 (SERVICE

    UNAVAILABLE) SELECT u.* FROM user u WHERE ud.id = 42
  36. Aplicação Usuário Banco de dados GET /api/users/42 HTTP 503 (SERVICE

    UNAVAILABLE) SELECT u.* FROM user u WHERE ud.id = 42
  37. Aplicação Usuário Banco de dados GET /api/users/42 HTTP 503 (SERVICE

    UNAVAILABLE) GET /api/users/42 SELECT u.* FROM user u WHERE ud.id = 42
  38. Aplicação Usuário Banco de dados GET /api/users/42 HTTP 503 (SERVICE

    UNAVAILABLE) GET /api/users/42 SELECT 1 FROM user u WHERE ud.id = 42 SELECT u.* FROM user u WHERE ud.id = 42
  39. Aplicação Usuário Banco de dados GET /api/users/42 HTTP 503 (SERVICE

    UNAVAILABLE) GET /api/users/42 SELECT 1 FROM user u WHERE ud.id = 42 SELECT u.* FROM user u WHERE ud.id = 42
  40. Aplicação Usuário Banco de dados GET /api/users/42 HTTP 503 (SERVICE

    UNAVAILABLE) GET /api/users/42 SELECT 1 FROM user u WHERE ud.id = 42 HTTP 200 (OK) SELECT u.* FROM user u WHERE ud.id = 42
  41. Aplicação Usuário Banco de dados GET /api/users/42 SELECT 1 FROM

    user u WHERE ud.id = 42 SELECT u.* FROM user u WHERE ud.id = 42 GET /api/users/42 GET /api/users/42 5xx
  42. Aplicação Usuário Banco de dados GET /api/users/42 GET /api/users/42 SELECT

    1 FROM user u WHERE ud.id = 42 SELECT u.* FROM user u WHERE ud.id = 42 GET /api/users/42 GET /api/users/42 GET /api/users/42 5xx
  43. Aplicação Usuário Banco de dados GET /api/users/42 GET /api/users/42 SELECT

    1 FROM user u WHERE ud.id = 42 SELECT u.* FROM user u WHERE ud.id = 42 GET /api/users/42 GET /api/users/42 GET /api/users/42 GET /api/users/42 . . . 5xx
  44. Aplicação Usuário Banco de dados GET /api/users/42 5xx GET /api/users/42

    SELECT 1 FROM user u WHERE ud.id = 42 2xx SELECT u.* FROM user u WHERE ud.id = 42 GET /api/users/42 GET /api/users/42 GET /api/users/42 GET /api/users/42 . . .
  45. Aplicação Usuário Banco de dados GET /api/users/42 5xx GET /api/users/42

    SELECT 1 FROM user u WHERE ud.id = 42 2xx SELECT u.* FROM user u WHERE ud.id = 42 GET /api/users/42 GET /api/users/42 GET /api/users/42 GET /api/users/42 . . . Aplicação Usuário Banco de dados
  46. Aplicação Usuário Banco de dados GET /api/users/42 5xx GET /api/users/42

    SELECT 1 FROM user u WHERE ud.id = 42 2xx SELECT u.* FROM user u WHERE ud.id = 42 GET /api/users/42 GET /api/users/42 GET /api/users/42 GET /api/users/42 . . . 2xx GET /api/users/42 Aplicação Usuário Banco de dados
  47. Aplicação Usuário Banco de dados GET /api/users/42 5xx GET /api/users/42

    SELECT 1 FROM user u WHERE ud.id = 42 2xx SELECT u.* FROM user u WHERE ud.id = 42 GET /api/users/42 GET /api/users/42 GET /api/users/42 GET /api/users/42 . . . ID NAME EMAIL ... ... ... 42 Gleice Elen [email protected] ... ... ... user Aplicação Usuário Banco de dados
  48. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  49. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  50. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  51. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  52. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  53. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  54. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  55. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  56. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  57. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  58. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  59. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  60. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  61. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  62. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  63. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  64. Aplicação Usuário Banco de dados INSERT INTO user (name ,

    email) VALUES (:name, :email); POST /api/users { name: “Gleice Elen”, email: “[email protected]” }
  65. Aplicação Usuário Banco de dados INSERT INTO user (name ,

    email) VALUES (:name, :email); POST /api/users { name: “Gleice Elen”, email: “[email protected]” }
  66. Aplicação Usuário Banco de dados INSERT INTO user (name ,

    email) VALUES (:name, :email); POST /api/users { name: “Gleice Elen”, email: “[email protected]” } INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action);
  67. Aplicação Usuário Banco de dados INSERT INTO user (name ,

    email) VALUES (:name, :email); POST /api/users { name: “Gleice Elen”, email: “[email protected]” } INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action);
  68. Aplicação Usuário Banco de dados INSERT INTO user (name ,

    email) VALUES (:name, :email); POST /api/users { name: “Gleice Elen”, email: “[email protected]” } Network INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action);
  69. Aplicação Usuário Banco de dados INSERT INTO user (name ,

    email) VALUES (:name, :email); POST /api/users { name: “Gleice Elen”, email: “[email protected]” } Network INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action);
  70. Aplicação Usuário Banco de dados INSERT INTO user (name ,

    email) VALUES (:name, :email); POST /api/users { name: “Gleice Elen”, email: “[email protected]” } POST /api/users { name: “Gleice Elen”, email: “[email protected]” } Network INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action);
  71. Aplicação Usuário Banco de dados INSERT INTO user (name ,

    email) VALUES (:name, :email); POST /api/users { name: “Gleice Elen”, email: “[email protected]” } POST /api/users { name: “Gleice Elen”, email: “[email protected]” } INSERT INTO user (name , email) VALUES (:name, :email); Network INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action);
  72. Aplicação Usuário Banco de dados INSERT INTO user (name ,

    email) VALUES (:name, :email); POST /api/users { name: “Gleice Elen”, email: “[email protected]” } POST /api/users { name: “Gleice Elen”, email: “[email protected]” } INSERT INTO user (name , email) VALUES (:name, :email); Network INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action);
  73. Aplicação Usuário Banco de dados INSERT INTO user (name ,

    email) VALUES (:name, :email); POST /api/users { name: “Gleice Elen”, email: “[email protected]” } POST /api/users { name: “Gleice Elen”, email: “[email protected]” } INSERT INTO user (name , email) VALUES (:name, :email); INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action); Network INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action);
  74. Aplicação Usuário Banco de dados INSERT INTO user (name ,

    email) VALUES (:name, :email); POST /api/users { name: “Gleice Elen”, email: “[email protected]” } POST /api/users { name: “Gleice Elen”, email: “[email protected]” } INSERT INTO user (name , email) VALUES (:name, :email); INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action); Network INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action);
  75. Aplicação Usuário Banco de dados INSERT INTO user (name ,

    email) VALUES (:name, :email); POST /api/users { name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } INSERT INTO user (name , email) VALUES (:name, :email); INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action); Network INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action);
  76. Aplicação Usuário Banco de dados INSERT INTO user (name ,

    email) VALUES (:name, :email); POST /api/users { name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } INSERT INTO user (name , email) VALUES (:name, :email); INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action); Network INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action); Aplicação Usuário Banco de dados
  77. Aplicação Usuário Banco de dados INSERT INTO user (name ,

    email) VALUES (:name, :email); POST /api/users { name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } INSERT INTO user (name , email) VALUES (:name, :email); INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action); Network INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action); Aplicação Usuário Banco de dados POST /api/users { name: “Gleice Elen”, email: “[email protected]” } 2xx
  78. Aplicação Usuário Banco de dados INSERT INTO user (name ,

    email) VALUES (:name, :email); POST /api/users { name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } INSERT INTO user (name , email) VALUES (:name, :email); INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action); Network INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action); Aplicação Usuário Banco de dados Banco de dados INSERT INTO user (name , email) VALUES (:name, :email); INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action); Banco de dados
  79. Aplicação Usuário Banco de dados INSERT INTO user (name ,

    email) VALUES (:name, :email); POST /api/users { name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } INSERT INTO user (name , email) VALUES (:name, :email); INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action); Network INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action); Aplicação Usuário Banco de dados Banco de dados INSERT INTO user (name , email) VALUES (:name, :email); INSERT INTO user (name , email) VALUES (:name, :email); INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action); INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action); Banco de dados 👀
  80. Aplicação Usuário Banco de dados INSERT INTO user (name ,

    email) VALUES (:name, :email); POST /api/users { name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } INSERT INTO user (name , email) VALUES (:name, :email); INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action); Network INSERT INTO user_audit (used_id, action) VALUES (:user_id, :action); Aplicação Usuário ID NAME EMAIL ... ... ... 42 Gleice Elen [email protected] 43 Gleice Elen [email protected] ... ... ... user 😱 Banco de dados
  81. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  82. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  83. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } } Nosso endpoint NÃO é idempotente! 😰
  84. @Entity public class User { @Id @GeneratedValue private Long id;

    private String name; private String email; private LocalDateTime createdAt; // construtor, getters etc }
  85. @Entity public class User { @Id @GeneratedValue private Long id;

    private String name; private String email; private LocalDateTime createdAt; // construtor, getters etc }
  86. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { throw new ResponseStatusException(BAD_REQUEST, "User exists”); // http 400 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  87. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { throw new ResponseStatusException(BAD_REQUEST, "User exists”); // http 400 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  88. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { throw new ResponseStatusException(BAD_REQUEST, "User exists”); // http 400 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } } 🥳 Email como chave natural de idempotência!
  89. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { throw new ResponseStatusException(BAD_REQUEST, "User exists”); // http 400 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  90. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { throw new ResponseStatusException(BAD_REQUEST, "User exists”); // http 400 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } } 😵💫 Not cool!
  91. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { throw new ResponseStatusException(BAD_REQUEST, "User exists”); // http 400 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  92. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { return ResponseEntity.ok("User exists”); // http 200 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  93. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { return ResponseEntity.ok("User exists”); // http 200 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } } 😍 Oh yeah!
  94. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user…
  95. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_…
  96. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_…
  97. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… Network
  98. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… Network
  99. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… Network POST /api/users { name: “Gleice Elen”, email: “[email protected]” }
  100. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… Network POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email );
  101. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… Network POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email );
  102. Aplicação Usuário Banco de dados INSERT user… POST /api/users {

    name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email ); Network INSERT user_audit_… SELECT exists…
  103. Aplicação Usuário Banco de dados INSERT user… POST /api/users {

    name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email ); Network INSERT user_audit_… SELECT exists… Aplicação Usuário Banco de dados
  104. Aplicação Usuário Banco de dados INSERT user… POST /api/users {

    name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email ); Network INSERT user_audit_… SELECT exists… Aplicação Usuário Banco de dados POST /api/users { name: “Gleice Elen”, email: “[email protected]” } 2xx
  105. Aplicação Usuário Banco de dados INSERT user… POST /api/users {

    name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email ); Network INSERT user_audit_… SELECT exists… Aplicação Usuário Banco de dados INSERT user… INSERT user_audit_… SELECT exists…
  106. Aplicação Usuário Banco de dados INSERT user… POST /api/users {

    name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email ); Network INSERT user_audit_… SELECT exists… Aplicação Usuário Banco de dados INSERT user… INSERT user_audit_… SELECT exists… SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email ); 💪
  107. Aplicação Usuário Banco de dados INSERT user… POST /api/users {

    name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email ); Network INSERT user_audit_… SELECT exists… Aplicação Usuário Banco de dados ID NAME EMAIL ... ... ... 42 Gleice Elen [email protected] ... ... ... user 🥳
  108. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user…
  109. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_…
  110. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_…
  111. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_…
  112. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 5xx
  113. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 5xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” }
  114. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 5xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email );
  115. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 5xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email );
  116. Aplicação Usuário Banco de dados INSERT user… POST /api/users {

    name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email ); INSERT user_audit_… SELECT exists… 5xx
  117. Aplicação Usuário Banco de dados INSERT user… POST /api/users {

    name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email ); INSERT user_audit_… SELECT exists… 5xx Aplicação Usuário Banco de dados
  118. Aplicação Usuário Banco de dados INSERT user… POST /api/users {

    name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email ); INSERT user_audit_… SELECT exists… 5xx Aplicação Usuário Banco de dados POST /api/users { name: “Gleice Elen”, email: “[email protected]” } 2xx
  119. Aplicação Usuário Banco de dados INSERT user… POST /api/users {

    name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email ); INSERT user_audit_… SELECT exists… 5xx Aplicação Usuário Banco de dados SELECT exists… INSERT user… INSERT user_audit_…
  120. Aplicação Usuário Banco de dados INSERT user… POST /api/users {

    name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email ); INSERT user_audit_… SELECT exists… 5xx Aplicação Usuário Banco de dados SELECT exists… INSERT user… INSERT user_audit_… SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email );
  121. Aplicação Usuário Banco de dados INSERT user… POST /api/users {

    name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email ); INSERT user_audit_… SELECT exists… 5xx Aplicação Usuário Banco de dados ID NAME EMAIL ... ... ... 42 Gleice Elen [email protected] ... ... ... user
  122. Aplicação Usuário Banco de dados INSERT user… POST /api/users {

    name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email ); INSERT user_audit_… SELECT exists… 5xx Aplicação Usuário Banco de dados ID NAME EMAIL ... ... ... 42 Gleice Elen [email protected] ... ... ... user ID ACTION USER_ID ... ... ... ?? ?? ?? ... ... ... user_audit
  123. Aplicação Usuário Banco de dados INSERT user… POST /api/users {

    name: “Gleice Elen”, email: “[email protected]” } 2xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists ( SELECT 1 FROM user u WHERE u.email = :email ); INSERT user_audit_… SELECT exists… 5xx Aplicação Usuário Banco de dados ID NAME EMAIL ... ... ... 42 Gleice Elen [email protected] ... ... ... user ID ACTION USER_ID ... ... ... ?? ?? ?? ... ... ... user_audit 😱
  124. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { return ResponseEntity.ok("User exists”); // http 200 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  125. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { return ResponseEntity.ok("User exists”); // http 200 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  126. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { return ResponseEntity.ok("User exists”); // http 200 }; User user = request.toModel(); repository.save(user); // inicia e comita uma transação UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); // inicia e comita uma transação return ResponseEntity .status(CREATED).build(); // http 201 } }
  127. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { return ResponseEntity.ok("User exists”); // http 200 }; User user = request.toModel(); repository.save(user); // inicia e comita uma transação UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); // inicia e comita uma transação return ResponseEntity .status(CREATED).build(); // http 201 } } Semelhante a auto_commit = true 😱
  128. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { return ResponseEntity.ok("User exists”); // http 200 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  129. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { return ResponseEntity.ok("User exists”); // http 200 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  130. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { return ResponseEntity.ok("User exists”); // http 200 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  131. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { return ResponseEntity.ok("User exists”); // http 200 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  132. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { return ResponseEntity.ok("User exists”); // http 200 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  133. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @Transactional @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { return ResponseEntity.ok("User exists”); // http 200 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  134. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @Transactional @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { return ResponseEntity.ok("User exists”); // http 200 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  135. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @Transactional @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { return ResponseEntity.ok("User exists”); // http 200 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  136. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user…
  137. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_…
  138. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_…
  139. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_…
  140. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_…
  141. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 5xx
  142. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 5xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” }
  143. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 5xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” }
  144. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 5xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_…
  145. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 5xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_…
  146. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 5xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_…
  147. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 5xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } 2xx SELECT exists… INSERT user… INSERT user_audit_…
  148. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 5xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 2xx Aplicação Usuário Banco de dados
  149. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 5xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 2xx Aplicação Usuário Banco de dados POST /api/users { name: “Gleice Elen”, email: “[email protected]” } 2xx
  150. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 5xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 2xx Aplicação Usuário Banco de dados SELECT exists… INSERT user… INSERT user_audit_…
  151. Aplicação Usuário Banco de dados POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 5xx POST /api/users { name: “Gleice Elen”, email: “[email protected]” } SELECT exists… INSERT user… INSERT user_audit_… 2xx Aplicação Usuário Banco de dados ID NAME EMAIL ... ... ... 42 Gleice Elen [email protected] ... ... ... user ID ACTION USER_ID ... ... ... 1024 created 42 ... ... ... user_audit 💪
  152. Aplicação Usuário 1 Usuário 2 POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } POST /api/users { name: “Gleice Elen”, email: “[email protected]” }
  153. Aplicação Usuário 1 Usuário 2 HTTP 201 (CREATED) POST /api/users

    { name: “Gleice Elen”, email: “[email protected]” } POST /api/users { name: “Gleice Elen”, email: “[email protected]” }
  154. Aplicação Usuário 1 Usuário 2 HTTP 201 (CREATED) HTTP 201

    (CREATED) POST /api/users { name: “Gleice Elen”, email: “[email protected]” } POST /api/users { name: “Gleice Elen”, email: “[email protected]” }
  155. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @Transactional @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { return ResponseEntity.ok("User exists”); // http 200 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  156. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @Transactional @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { return ResponseEntity.ok("User exists”); // http 200 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  157. @RestController public class CreateNewUserController { private UserRepository repository; private UserAuditRepository

    auditRepository; @Transactional @PostMapping(“/api/users") public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { if (repository.existsByEmail(request.email())) { return ResponseEntity.ok("User exists”); // http 200 }; User user = request.toModel(); repository.save(user); UserAudit audit = new UserAudit(user, "created"); auditRepository.save(audit); return ResponseEntity .status(CREATED).build(); // http 201 } }
  158. @Entity public class User { @Id @GeneratedValue private Long id;

    private String name; private String email; private LocalDateTime createdAt; // construtor, getters etc }
  159. @Entity public class User { @Id @GeneratedValue private Long id;

    private String name; private String email; private LocalDateTime createdAt; // construtor, getters etc }
  160. @Entity public class User { @Id @GeneratedValue private Long id;

    private String name; @Column(unique = true) private String email; private LocalDateTime createdAt; // construtor, getters etc }
  161. Aplicação Usuário 1 Usuário 2 POST /api/users { name: “Gleice

    Elen”, email: “[email protected]” } POST /api/users { name: “Gleice Elen”, email: “[email protected]” }
  162. Aplicação Usuário 1 Usuário 2 HTTP 201 (CREATED) POST /api/users

    { name: “Gleice Elen”, email: “[email protected]” } POST /api/users { name: “Gleice Elen”, email: “[email protected]” }
  163. Aplicação Usuário 1 Usuário 2 HTTP 201 (CREATED) HTTP 409

    (CONFLICT) POST /api/users { name: “Gleice Elen”, email: “[email protected]” } POST /api/users { name: “Gleice Elen”, email: “[email protected]” }
  164. Aplicação Usuário Banco de dados POST /api/users 2xx OK SELECT

    exists… INSERT user… INSERT user_audit_…
  165. Aplicação Usuário Banco de dados POST /api/users SELECT exists… INSERT

    user… INSERT user_audit_… OK 2xx Aplicação Usuário Banco de dados
  166. Aplicação Usuário Banco de dados POST /api/users SELECT exists… INSERT

    user… INSERT user_audit_… OK 2xx Aplicação Usuário Banco de dados OK SELECT exists… INSERT user… INSERT user_audit_… Unit of Work
  167. Aplicação Usuário Banco de dados POST /api/users SELECT exists… INSERT

    user… INSERT user_audit_… OK 2xx Aplicação Usuário Banco de dados POST /api/users 2xx
  168. Aplicação Usuário Banco de dados POST /api/users SELECT exists… INSERT

    user… INSERT user_audit_… OK 2xx Aplicação Usuário Banco de dados 2xx POST /api/users Unit of Work
  169. Aplicação Usuário Banco de dados POST /api/users SELECT exists… INSERT

    user… INSERT user_audit_… OK 2xx Aplicação Usuário Banco de dados OK SELECT exists… INSERT user… INSERT user_audit_… 2xx POST /api/users Unit of Work Unit of Work
  170. Aplicação Usuário Banco de dados POST /api/users SELECT exists… INSERT

    user… INSERT user_audit_… OK 2xx Aplicação Usuário Banco de dados OK SELECT exists… INSERT user… INSERT user_audit_… 2xx POST /api/users Unit of Work Unit of Work
  171. Aplicação Usuário Banco de dados POST /api/users SELECT exists… INSERT

    user… INSERT user_audit_… OK 2xx Usuário Banco de dados OK SELECT exists… INSERT user… INSERT user_audit_… POST /api/users 2xx Unit of Work
  172. @RestController public class CreateNewUserController { private UserRepository repository; @Transactional @PostMapping(“/api/users")

    public ResponseEntity<?> create(@Valid @RequestBody NewUserRequest request) { // lógica de negócio return ResponseEntity .status(OK).build(); // http 200 } }
  173. …abraça o tudo ou nada …permite re-tentativas do cliente …falha

    rapidamente …previne falhas de races conditions