21 Einbinden eines Contracts

21.1 Einführung

Das Einbinden eines Contracts ist ein zentraler Schritt in der Contract-First-Entwicklung, bei dem eine vorab definierte API-Spezifikation in ein bestehendes Projekt integriert wird. Dieser Prozess gewährleistet, dass die Implementierung der API strikt dem definierten Vertrag entspricht, was Konsistenz, Wartbarkeit und Zusammenarbeit zwischen verschiedenen Teams fördert. In diesem Kapitel erläutern wir die Schritte zur Einbindung eines Contracts in eine Spring Boot-Anwendung, illustrieren den Prozess mit praktischen Beispielen und geben Best Practices zur erfolgreichen Integration.

21.2 Schritte zur Einbindung eines Contracts

  1. Erstellung oder Erhalt der API-Spezifikation

  2. Nutzung von Tools zur Code-Generierung

  3. Integration in das Spring Boot-Projekt

  4. Implementierung der Geschäftslogik

  5. Validierung und Testing

21.3 Praktisches Beispiel: Einbinden eines OpenAPI Contracts in Spring Boot

Wir demonstrieren den Prozess anhand einer einfachen Buchverwaltung-API.

21.3.1 Schritt 1: Erstellung der OpenAPI-Spezifikation

Erstellen Sie eine Datei namens api.yaml im Verzeichnis src/main/resources/ mit folgendem Inhalt:

openapi: 3.0.1
info:
  title: Bücher API
  description: Eine API zur Verwaltung von Büchern.
  version: 1.0.0
servers:
  - url: http://localhost:8080/api
paths:
  /books:
    get:
      summary: Liste aller Bücher abrufen
      responses:
        '200':
          description: Erfolgreiche Antwort
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Book'
    post:
      summary: Ein neues Buch erstellen
      requestBody:
        description: Buchdaten
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Book'
      responses:
        '201':
          description: Buch erstellt
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Book'
  /books/{id}:
    get:
      summary: Ein Buch anhand der ID abrufen
      parameters:
        - in: path
          name: id
          schema:
            type: integer
            format: int64
          required: true
          description: Die ID des Buches
      responses:
        '200':
          description: Erfolgreiche Antwort
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Book'
        '404':
          description: Buch nicht gefunden
components:
  schemas:
    Book:
      type: object
      properties:
        id:
          type: integer
          format: int64
        title:
          type: string
        author:
          type: string
      required:
        - title
        - author

21.3.2 Schritt 2: Konfiguration des OpenAPI Generators

Fügen Sie das OpenAPI Generator Maven Plugin zu Ihrer pom.xml hinzu:

<build>
    <plugins>
        <!-- OpenAPI Generator Plugin -->
        <plugin>
            <groupId>org.openapitools</groupId>
            <artifactId>openapi-generator-maven-plugin</artifactId>
            <version>6.3.0</version>
            <executions>
                <execution>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                    <configuration>
                        <inputSpec>${project.basedir}/src/main/resources/api.yaml</inputSpec>
                        <generatorName>spring</generatorName>
                        <output>${project.build.directory}/generated-sources/openapi</output>
                        <configOptions>
                            <interfaceOnly>true</interfaceOnly>
                            <delegatePattern>true</delegatePattern>
                        </configOptions>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

21.3.3 Schritt 3: Code-Generierung ausführen

Führen Sie den folgenden Maven-Befehl aus, um den Code basierend auf der OpenAPI-Spezifikation zu generieren:

mvn clean install

Dies erzeugt die Server-Stubs und Modelle im Verzeichnis ${project.build.directory}/generated-sources/openapi.

21.3.4 Schritt 4: Implementierung der Geschäftslogik

Implementieren Sie die generierten Controller-Stubs mit der Geschäftslogik.

BookControllerImpl.java

package com.example.generated.api;

import com.example.generated.model.Book;
import com.example.generated.repository.BookRepository;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;

import java.util.List;
import java.util.Optional;

@Controller
public class BookControllerImpl implements BookController {

    private final BookRepository bookRepository;

    public BookControllerImpl(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }

    @Override
    public ResponseEntity<List<Book>> getBooks() {
        List<Book> books = bookRepository.findAll();
        return ResponseEntity.ok(books);
    }

    @Override
    public ResponseEntity<Book> getBookById(Long id) {
        Optional<Book> book = bookRepository.findById(id);
        return book.map(ResponseEntity::ok)
                   .orElse(ResponseEntity.notFound().build());
    }

    @Override
    public ResponseEntity<Book> createBook(Book book) {
        Book savedBook = bookRepository.save(book);
        return ResponseEntity.status(201).body(savedBook);
    }

    // Weitere Methoden (updateBook, deleteBook etc.)
}

21.3.5 Schritt 5: Erstellung des Datenmodells und Repository

21.3.5.1 Book.java

package com.example.generated.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Book {
    private Long id;
    private String title;
    private String author;
}

21.3.5.2 BookRepository.java

package com.example.generated.repository;

import com.example.generated.model.Book;
import org.springframework.stereotype.Repository;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@Repository
public class BookRepository {
    private final Map<Long, Book> bookStore = new ConcurrentHashMap<>();
    private final AtomicLong idGenerator = new AtomicLong();

    public Book save(Book book) {
        if (book.getId() == null) {
            book.setId(idGenerator.incrementAndGet());
        }
        bookStore.put(book.getId(), book);
        return book;
    }

    public Optional<Book> findById(Long id) {
        return Optional.ofNullable(bookStore.get(id));
    }

    public List<Book> findAll() {
        return new ArrayList<>(bookStore.values());
    }

    public boolean deleteById(Long id) {
        return bookStore.remove(id) != null;
    }
}

21.3.6 Schritt 6: Testen der API

Nach der Implementierung können Sie die API testen, indem Sie Anfragen an die definierten Endpunkte senden. Nutzen Sie Tools wie Postman, cURL oder die automatisch generierte Swagger UI unter http://localhost:8080/swagger-ui.html, um die API zu erkunden und zu testen.

21.3.6.1 Beispiel: Erstellen eines neuen Buches mit cURL

curl -X POST "http://localhost:8080/api/books" \
     -H "Content-Type: application/json" \
     -d '{"title": "Spring Boot in Action", "author": "Craig Walls"}'

21.3.6.2 Antwort

{
  "id": 1,
  "title": "Spring Boot in Action",
  "author": "Craig Walls"
}

21.4 Best Practices für das Einbinden eines Contracts

  1. Detaillierte und klare Spezifikation erstellen

  2. Wiederverwendbare Komponenten definieren

  3. Automatisierte Code-Generierung integrieren

  4. Versionierung und Deprecation planen

  5. Implementierung von Sicherheitsmechanismen

  6. Regelmäßige Validierung und Synchronisation

  7. Dokumentation und Schulung

  8. Effiziente Fehlerbehandlung

21.5 Tools und Ressourcen

21.6 tl;dr

Das Einbinden eines Contracts ist ein essenzieller Schritt in der Contract-First-Entwicklung, bei dem eine vorab definierte API-Spezifikation in ein Spring Boot-Projekt integriert wird. Durch die Nutzung von Tools wie dem OpenAPI Generator können Server-Stubs und Modelle automatisch aus der Spezifikation generiert werden, was die Konsistenz und Qualität der API sicherstellt. Die Implementierung der Geschäftslogik erfolgt in den generierten Stubs, während die Spezifikation als zentrale Quelle für Dokumentation und Validierung dient. Best Practices wie klare Spezifikationen, Wiederverwendbarkeit, automatisierte Tests, Versionierung und Sicherheitsimplementierungen gewährleisten eine effiziente und wartbare API-Entwicklung. Insgesamt fördert das Einbinden eines Contracts die Zusammenarbeit, reduziert Fehler und erhöht die Wartbarkeit und Skalierbarkeit von APIs.