システム概要
本システムは、Spring Bootをバックエンド、Vue.jsをフロントエンドとして実装した不動産賃貸管理プラットフォームです。UniAppを活用することで、Webアプリケーションとモバイルアプリの両方に対応した統一的なユーザー体験を提供します。
技術アーキテクチャ
バックエンド:Spring Bootフレームワーク
Spring Bootは、自動設定機能により開発効率を大幅に向上させます。内蔵サーバーにより、追加設定なしでアプリケーションを即座に実行できます。Spring Data JPAやSpring Securityとの統合により、堅牢なエンタープライズアプリケーションを迅速に構築可能です。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
フロントエンド:Vue.jsエコシステム
Vue.jsのリアクティブデータバインディングとコンポーネントベースのアーキテクチャにより、インタラクティブなUIを効率的に実装できます。Vuexを使用した状態管理とVue Routerによるルーティング機能で、スケーラブルなシングルページアプリケーションを構築します。
<template>
<div class="property-list">
<div v-for="property in filteredProperties"
:key="property.id"
class="property-card"
@click="showDetails(property)">
<img :src="property.image" :alt="property.title">
<h3>{{ property.title }}</h3>
<p>¥{{ formatPrice(property.price) }}/月</p>
<div class="property-features">
<span v-for="feature in property.features" :key="feature">
{{ feature }}
</span>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'PropertyList',
data() {
return {
properties: [],
filters: {
priceRange: [0, 100000],
location: '',
propertyType: 'all'
}
}
},
computed: {
filteredProperties() {
return this.properties.filter(property => {
return property.price >= this.filters.priceRange[0] &&
property.price <= this.filters.priceRange[1] &&
(this.filters.location === '' || property.location.includes(this.filters.location))
})
}
},
methods: {
async fetchProperties() {
try {
const response = await this.$http.get('/api/properties')
this.properties = response.data
} catch (error) {
console.error('物件データの取得に失敗しました:', error)
}
},
formatPrice(price) {
return price.toLocaleString('ja-JP')
},
showDetails(property) {
this.$router.push(`/properties/${property.id}`)
}
},
mounted() {
this.fetchProperties()
}
}
</script>
データベース設計:MySQLとMyBatis
MyBatisを使用することで、SQL文をJavaコードから分離し、柔軟なデータアクセス層を実現します。XMLマッピングファイルにより、複雑なクエリも簡単に管理できます。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rental.mapper.PropertyMapper">
<resultMap id="PropertyResultMap" type="com.rental.entity.Property">
<id property="id" column="id"/>
<result property="title" column="title"/>
<result property="description" column="description"/>
<result property="price" column="price"/>
<result property="address" column="address"/>
<result property="area" column="area"/>
<result property="rooms" column="rooms"/>
<result property="status" column="status"/>
<association property="owner" javaType="com.rental.entity.User">
<id property="id" column="owner_id"/>
<result property="name" column="owner_name"/>
<result property="phone" column="owner_phone"/>
</association>
</resultMap>
<select id="findByFilters" resultMap="PropertyResultMap">
SELECT p.*, u.name as owner_name, u.phone as owner_phone
FROM properties p
LEFT JOIN users u ON p.owner_id = u.id
WHERE 1=1
<if test="minPrice != null">
AND p.price >= #{minPrice}
</if>
<if test="maxPrice != null">
AND p.price <= #{maxPrice}
</if>
<if test="location != null and location != ''">
AND p.address LIKE CONCAT('%', #{location}, '%')
</if>
<if test="propertyType != null and propertyType != ''">
AND p.property_type = #{propertyType}
</if>
ORDER BY p.created_at DESC
</select>
</mapper>
主要機能モジュール
物件管理機能
物件の登録、編集、削除、検索機能を実装。画像アップロード、詳細説明、価格設定、施設情報の管理が可能です。
予約管理システム
予約カレンダー、空室状況のリアルタイム更新、予約申請の承認・拒否機能を提供します。
@RestController
@RequestMapping("/api/bookings")
public class BookingController {
@Autowired
private BookingService bookingService;
@PostMapping("/create")
public ResponseEntity<ApiResponse> createBooking(
@RequestBody BookingRequest request,
@AuthenticationPrincipal UserDetails userDetails) {
try {
Booking booking = bookingService.createBooking(
request.getPropertyId(),
userDetails.getUsername(),
request.getCheckInDate(),
request.getCheckOutDate()
);
return ResponseEntity.ok(new ApiResponse("予約が正常に作成されました", booking));
} catch (PropertyNotAvailableException e) {
return ResponseEntity.badRequest()
.body(new ApiResponse("指定の日付は予約できません", null));
}
}
@GetMapping("/my-bookings")
public ResponseEntity<List<Booking>> getUserBookings(
@AuthenticationPrincipal UserDetails userDetails) {
List<Booking> bookings = bookingService.getUserBookings(userDetails.getUsername());
return ResponseEntity.ok(bookings);
}
}
決済連携
外部決済サービス(StripeやSquare)との連携により、安全なオンライン決済を実現します。
システムテスト戦略
単体テスト
JUnit 5とMockitoを使用して、各サービス層のビジネスロジックを網羅的にテストします。
@ExtendWith(MockitoExtension.class)
class PropertyServiceTest {
@Mock
private PropertyRepository propertyRepository;
@InjectMocks
private PropertyService propertyService;
@Test
void shouldReturnAvailableProperties() {
// Given
Property property1 = new Property(1L, "物件A", 50000, "東京");
Property property2 = new Property(2L, "物件B", 70000, "大阪");
List<Property> expectedProperties = Arrays.asList(property1, property2);
when(propertyRepository.findByStatus("AVAILABLE")).thenReturn(expectedProperties);
// When
List<Property> actualProperties = propertyService.getAvailableProperties();
// Then
assertEquals(expectedProperties.size(), actualProperties.size());
assertEquals(expectedProperties, actualProperties);
verify(propertyRepository, times(1)).findByStatus("AVAILABLE");
}
}
統合テスト
Spring Boot Testを使用して、コントローラーからデータベースまでの全体フローをテストします。
E2Eテスト
CypressまたはSeleniumを使用して、ユーザーの操作シナリオを再現し、UIの正確性を検証します。
デプロイメント構成
Dockerコンテナ化
FROM openjdk:11-jre-slim
COPY target/rental-system.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]
Kubernetesマニフェスト
apiVersion: apps/v1
kind: Deployment
metadata:
name: rental-backend
spec:
replicas: 3
selector:
matchLabels:
app: rental-backend
template:
metadata:
labels:
app: rental-backend
spec:
containers:
- name: backend
image: rental-system:latest
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: "production"
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: rental-config
key: db.host
データベーススキーマ
-- 物件テーブル
CREATE TABLE properties (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(200) NOT NULL,
description TEXT,
price DECIMAL(10,2) NOT NULL,
address VARCHAR(500) NOT NULL,
area DECIMAL(8,2),
rooms INT,
property_type ENUM('APARTMENT', 'HOUSE', 'STUDIO') DEFAULT 'APARTMENT',
status ENUM('AVAILABLE', 'RENTED', 'MAINTENANCE') DEFAULT 'AVAILABLE',
owner_id BIGINT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (owner_id) REFERENCES users(id)
);
-- 予約テーブル
CREATE TABLE bookings (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
property_id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL,
check_in_date DATE NOT NULL,
check_out_date DATE NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
status ENUM('PENDING', 'CONFIRMED', 'CANCELLED') DEFAULT 'PENDING',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (property_id) REFERENCES properties(id),
FOREIGN KEY (tenant_id) REFERENCES users(id)
);