Skip to content

Instantly share code, notes, and snippets.

@mutukrish
Last active April 3, 2025 05:24
Show Gist options
  • Save mutukrish/7245a99f6c795cf79b6bb455db88789e to your computer and use it in GitHub Desktop.
Save mutukrish/7245a99f6c795cf79b6bb455db88789e to your computer and use it in GitHub Desktop.
Migration plan for Petclinic

Spring PetClinic MongoDB Migration Plan

1. Current Application Overview

The Spring PetClinic application is a sample Spring Boot application that demonstrates the use of Spring's core features, particularly in the context of data access. Currently, it uses a relational database (H2, HSQLDB, MySQL or PostgreSQL) with Spring Data JPA.

Key components of the current architecture:

  • Entity Model:

    • Owner: Pet owners with contact information
    • Pet: Pets owned by owners
    • Visit: Record of a pet's visit to the clinic
    • Vet: Veterinarians working at the clinic
    • Specialty: Specialties that vets may have
  • Repositories:

    • JPA repositories for each entity
    • Custom query methods for specific business requirements
  • Database Configuration:

    • Spring Data JPA configuration
    • Relational database schema with tables for each entity
    • Foreign key relationships between tables

2. MongoDB Migration Strategy

2.1 Schema Design

When migrating from a relational database to MongoDB, we need to rethink our data model. MongoDB's document model allows for embedded documents and references between documents.

Proposed MongoDB Schema:

// Owner Collection
{
  _id: ObjectId("..."),
  firstName: "John",
  lastName: "Doe",
  address: "123 Main St",
  city: "Anytown",
  telephone: "555-1234",
  pets: [
    {
      _id: ObjectId("..."),
      name: "Fluffy",
      birthDate: ISODate("2018-01-01"),
      type: {
        _id: ObjectId("..."),
        name: "cat"
      },
      visits: [
        {
          _id: ObjectId("..."),
          date: ISODate("2023-01-15"),
          description: "Annual checkup",
          vet: ObjectId("...") // Reference to vet
        }
      ]
    }
  ]
}

// Vets Collection
{
  _id: ObjectId("..."),
  firstName: "Jane",
  lastName: "Smith",
  specialties: [
    {
      _id: ObjectId("..."),
      name: "radiology"
    },
    {
      _id: ObjectId("..."),
      name: "surgery"
    }
  ]
}

// PetTypes Collection (reference data)
{
  _id: ObjectId("..."),
  name: "cat"
}

// Specialties Collection (reference data)
{
  _id: ObjectId("..."),
  name: "radiology"
}

Design Decisions:

  1. Embedding vs. Referencing:

    • Pet documents are embedded within Owner documents since they have a strong parent-child relationship and are often accessed together.
    • Visit documents are embedded within Pet documents for the same reason.
    • Vet references in Visit documents use references rather than embedding to avoid data duplication.
    • PetTypes and Specialties are stored in separate collections as they're reference data.
  2. Indexing Strategy:

    • Create indexes on commonly queried fields like Owner.lastName, Owner.telephone
    • Create compound indexes for multi-field queries

2.2 Files to Change

Java Model Classes

  1. Replace JPA annotations with MongoDB annotations:

File: src/main/java/org/springframework/samples/petclinic/owner/Owner.java

import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.annotation.Id;

@Document(collection = "owners")
public class Owner {
    @Id
    private String id;
    
    @Field("first_name")
    private String firstName;
    
    @Field("last_name")
    private String lastName;
    
    // Other fields...
    
    // Embed pets directly
    private List<Pet> pets;
}

File: src/main/java/org/springframework/samples/petclinic/owner/Pet.java

// No @Document annotation needed as it will be embedded
public class Pet {
    @Id
    private String id;
    
    private String name;
    
    @Field("birth_date")
    private LocalDate birthDate;
    
    // Reference to PetType
    @DBRef
    private PetType type;
    
    // Embed visits
    private List<Visit> visits;
}

File: src/main/java/org/springframework/samples/petclinic/visit/Visit.java

// No @Document annotation needed as it will be embedded
public class Visit {
    @Id
    private String id;
    
    private LocalDate date;
    
    private String description;
    
    // Reference to Vet
    @DBRef
    private Vet vet;
}

File: src/main/java/org/springframework/samples/petclinic/vet/Vet.java

@Document(collection = "vets")
public class Vet {
    @Id
    private String id;
    
    @Field("first_name")
    private String firstName;
    
    @Field("last_name")
    private String lastName;
    
    @DBRef
    private Set<Specialty> specialties;
}

Repository Interfaces

  1. Replace JPA repositories with MongoDB repositories:

File: src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;

public interface OwnerRepository extends MongoRepository<Owner, String> {
    // Find owners by last name
    @Query("{ 'lastName': { $regex: ?0, $options: 'i' } }")
    Collection<Owner> findByLastName(String lastName);
    
    // Find owner by id
    @Override
    Optional<Owner> findById(String id);
}

File: src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java

import org.springframework.data.mongodb.repository.MongoRepository;

public interface VetRepository extends MongoRepository<Vet, String> {
}

Configuration Files

  1. Update Spring configuration for MongoDB:

File: src/main/resources/application.properties

# MongoDB Configuration
spring.data.mongodb.uri=mongodb://localhost:27017/petclinic

# Disable JPA
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
  1. Create MongoDB Configuration class:

File: src/main/java/org/springframework/samples/petclinic/config/MongoConfig.java

import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;

@Configuration
@EnableMongoRepositories(basePackages = "org.springframework.samples.petclinic")
public class MongoConfig extends AbstractMongoClientConfiguration {
    @Override
    protected String getDatabaseName() {
        return "petclinic";
    }
}
  1. Create MongoDB initialization script:

File: src/main/resources/db/mongodb/init-mongodb.js

// Create collections and indexes
db.createCollection("owners");
db.createCollection("vets");
db.createCollection("petTypes");
db.createCollection("specialties");

// Create indexes
db.owners.createIndex({ "lastName": 1 });
db.owners.createIndex({ "telephone": 1 });

// Insert pet types
db.petTypes.insertMany([
  { name: "cat" },
  { name: "dog" },
  { name: "lizard" },
  { name: "snake" },
  { name: "bird" },
  { name: "hamster" }
]);

// Insert specialties
db.specialties.insertMany([
  { name: "radiology" },
  { name: "surgery" },
  { name: "dentistry" }
]);
  1. Create data import utility:

File: src/main/java/org/springframework/samples/petclinic/config/MongoDataInitializer.java

import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.mongodb.core.MongoTemplate;

@Configuration
@Profile("init-mongodb")
public class MongoDataInitializer {
    @Bean
    public CommandLineRunner initMongoDb(MongoTemplate mongoTemplate) {
        return args -> {
            // Initialize MongoDB with sample data
            // ...
        };
    }
}

Service Layer Changes

  1. Update service classes to work with the new document model:

File: src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java

// Update controller methods to handle the new document structure
// Especially for adding pets and visits which are now embedded documents

File: src/main/java/org/springframework/samples/petclinic/owner/PetController.java

// Update to handle embedded documents

3. Implementation Steps

  1. Set up MongoDB environment:

    • Install MongoDB (local or Docker)
    • Configure Spring Boot to connect to MongoDB
  2. Create MongoDB domain model:

    • Update entity classes with MongoDB annotations
    • Implement embedded document structure
  3. Update repository interfaces:

    • Replace JPA repositories with MongoDB repositories
    • Update query methods with MongoDB queries
  4. Update service layer:

    • Modify service logic to handle the new document model
    • Update transaction boundaries
  5. Create data migration script:

    • Import existing SQL data to MongoDB
  6. Update tests:

    • Modify integration tests to use MongoDB
    • Add tests for MongoDB-specific functionality
  7. Update application configuration:

    • Remove JPA configuration
    • Add MongoDB configuration

4. Additional Considerations

4.1 Performance Optimization

  • Use appropriate indexes for queries
  • Consider read patterns when designing embedded documents
  • Batch operations for large data sets

4.2 Data Migration

  • Create a one-time migration script to move data from SQL to MongoDB
  • Validate data integrity after migration

4.3 Transaction Support

  • MongoDB supports multi-document transactions since version 4.0
  • Update service layer to use MongoDB transactions where needed

5. MongoDB Dependencies

Add these dependencies to the pom.xml file:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

6. Testing Strategy

  1. Unit tests:

    • Test repository methods with MongoDB test containers
    • Test service methods with mocked repositories
  2. Integration tests:

    • Use embedded MongoDB for testing
    • Test the entire flow from controllers to repositories
@SpringBootTest
@ExtendWith(SpringExtension.class)
@AutoConfigureDataMongo
class OwnerRepositoryTests {
    // Integration tests with MongoDB
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment