| name | database-per-service-example | ||||||
|---|---|---|---|---|---|---|---|
| description | Database-per-service sample that separates students and courses services into independent pluggable databases. | ||||||
| tags |
|
||||||
| blog_post | https://andersswanson.dev/2026/04/28/database-per-service-with-oracle-ai-database-pdbs/ |
This sample demonstrates the database-per-service pattern on Oracle AI Database with a simple cross-service workflow: course registration checks.
Each service owns its own pluggable database (PDB), schema, tables, and datasource:
students/: student profile and completed-course history instudentpdbcourses/: course catalog, prerequisites, and term offerings incoursepdbsample/: command-line runner and end-to-end test that compose data from both services over HTTP
The sample intentionally avoids cross-PDB joins, shared schemas, and database links. The registration check is computed at the application layer after fetching service-owned data from each boundary.
To keep the tutorial compact, each service uses one main JPA entity plus embedded collections. The model is intentionally small:
students: university ID, name, status, and completed course codescourses: course code, title, prerequisite course codes, and one offering per term
Owns:
studentsstudent_completed_courses
Exposes:
POST /studentsPOST /students/{studentId}/completed-coursesGET /students/{studentId}GET /students/{studentId}/completed-coursesGET /db-info
Owns:
course_catalogcourse_prerequisitescourse_offerings
Exposes:
POST /coursesPOST /courses/{courseCode}/prerequisitesPOST /course-offeringsGET /courses/{courseCode}GET /courses/{courseCode}/prerequisitesGET /course-offerings/{courseCode}?termCode=...GET /db-info
Contains:
com.example.sample.DatabasePerServiceSampleRunner- the Testcontainers-backed end-to-end test
- the SQL*Plus script that provisions the PDBs and schema objects
- Oracle AI Database can isolate service-owned data at the PDB boundary
- two Spring Boot services can use different PDB service names in the same Oracle AI Database instance
- a simple business decision can be composed without violating database ownership
The composed workflow is:
- Create a student profile in the
studentsservice - Add completed-course history in the
studentsservice - Create catalog and offering data in the
coursesservice - Read both service-owned datasets
- Check whether the student is ready to register in the sample runner
The runner keeps the decision small and sample-friendly:
- student status
- prerequisite completion
- available seat in the requested offering
Run the tests with Maven:
mvn -pl sample test -amThe end-to-end tests provision studentpdb and coursepdb inside an Oracle AI Database Free container, boot both Spring Boot applications, seed data through the service APIs, and verify ready and not-ready scenarios.
You'll see output similar to the following as the test scenarios run:
Scenario: student-is-eligible
Student: Alice Nguyen (ACTIVE)
Course: ELIGIBLE-CS404 - Distributed Systems
Completed courses: ELIGIBLE-CS201, ELIGIBLE-MATH101
Prerequisites satisfied: true
Seats available: true
Eligible: true
Reasons: Registration checks passed
Assertions passed
Scenario: student-missing-prereq
Student: Alice Nguyen (ACTIVE)
Course: MISSINGPREREQ-CS404 - Distributed Systems
Completed courses: MISSINGPREREQ-MATH101
Prerequisites satisfied: false
Seats available: true
Eligible: false
Reasons: Student is missing at least one required prerequisite
Assertions passed
- Start an Oracle AI Database Free container.
- Run the setup script in
sample/src/test/resources/create-pdbs.sqlassysdba. - Start the
studentsservice. - Start the
coursesservice. - Run the sample runner.
The setup script creates:
studentpdbcoursepdbstudents_appcourses_app- the
studentsservice tables - the
coursesservice tables
Start the students service:
mvn -f database-per-service-example/pom.xml -pl students spring-boot:run \
-DJDBC_URL=jdbc:oracle:thin:@localhost:1521/studentpdb \
-DUSERNAME=students_app \
-DPASSWORD=testpwd \
-DSERVER_PORT=8081Start the courses service:
mvn -f database-per-service-example/pom.xml -pl courses spring-boot:run \
-DJDBC_URL=jdbc:oracle:thin:@localhost:1521/coursepdb \
-DUSERNAME=courses_app \
-DPASSWORD=testpwd \
-DSERVER_PORT=8082Run the sample runner:
mvn -f database-per-service-example/pom.xml -pl sample exec:java \
-DSTUDENTS_BASE_URL=http://localhost:8081 \
-DCOURSES_BASE_URL=http://localhost:8082The runner seeds a registration scenario, reads both services back, and prints a report showing the decision plus proof that the services are connected to different PDBs.