Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ JWT_SECRET_KEY=replace-with-32-byte-or-longer-secret-key
JWT_ISSUER=stream-server
# Access Token 만료 시간 (ms)
JWT_ACCESS_TOKEN_EXPIRY=3600000

### infrastructure:db — MySQL ###
DB_URL=jdbc:mysql://localhost:3306/stream?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
DB_USERNAME=stream
DB_PASSWORD=replace-with-db-password
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ api/
└── app-api # 학생 앱 (STUDENT)
core/
├── common # ErrorCode/BusinessException, PrincipalProvider 등 공유 커널
└── domain/{도메인} # {Domain}Service(공개) / {Domain}ServiceImpl(internal)
└── domain/{모듈} # domain/{도메인}/{domain|repository|service|service.impl}, impl만 비공개
gateway/
├── auth # Spring Security, JWT
└── logging # MDC 필터, access log
Expand All @@ -32,7 +32,7 @@ infrastructure/
└── outbox # 아웃박스 릴레이 (폴러·재발행)
```

도메인 간 경계는 Gradle(컴파일 타임)로, 도메인 내부 공개/`internal` 경계는 `verify()`(CI)로 강제한다.
도메인 간 경계는 Gradle(컴파일 타임)로, 도메인 내부 `service.impl` 경계는 ArchUnit 테스트(CI)로 강제한다. `service.impl` 안의 클래스는 모두 package-private으로 둔다.

## 컨벤션 문서

Expand Down
2 changes: 0 additions & 2 deletions bootstrap/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,4 @@ dependencies {
testImplementation(libs.springModulithDocs)
testImplementation(libs.archunitJunit5)
testRuntimeOnly(libs.junitPlatformLauncher)
// infrastructure:db가 끌어오는 JPA 자동구성이 DataSource를 요구하므로 테스트 전용 인메모리 DB로 컨텍스트 로딩을 지원
testRuntimeOnly(libs.h2)
}
1 change: 1 addition & 0 deletions bootstrap/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ spring:
config:
import:
- classpath:application-gateway-auth.yml
- classpath:application-infrastructure-db.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package kr.ac.kookmin.stream;

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;

import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.core.domain.Dependency;
import com.tngtech.archunit.core.importer.ImportOption;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.lang.conditions.ArchConditions;

/**
* 도메인 모듈은 OPEN 모듈이라 Modulith verify()가 service.impl을 가리지 않는다.
* 대신 여기서 impl 패키지의 외부 참조와 public 노출을 막는다.
*/
@AnalyzeClasses(packages = "kr.ac.kookmin.stream", importOptions = ImportOption.DoNotIncludeTests.class)
class DomainImplAccessTests {

private static final String IMPL_PACKAGE = "..service.impl..";

@ArchTest
static final ArchRule implIsOnlyReferencedFromItsOwnPackage = classes()
.that().resideInAPackage(IMPL_PACKAGE)
.should(ArchConditions.onlyHaveDependentsWhere(originInSamePackageAsTarget()))
.because("service.impl 패키지는 같은 패키지 안에서만 참조한다");

@ArchTest
static final ArchRule implHasNoPublicClasses = noClasses()
.that().resideInAPackage(IMPL_PACKAGE)
.should().bePublic()
.because("service.impl 패키지의 클래스는 package-private으로 둔다");

private static DescribedPredicate<Dependency> originInSamePackageAsTarget() {
return new DescribedPredicate<>("origin is in the same package as target") {
@Override
public boolean test(Dependency dependency) {
return dependency.getOriginClass().getPackageName()
.equals(dependency.getTargetClass().getPackageName());
}
};
}
}

This file was deleted.

4 changes: 4 additions & 0 deletions core/domain/event/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ dependencies {
implementation(libs.springContext)
implementation(libs.springTx)

// @ApplicationModule(type = OPEN) 선언용(package-info). 경계 검증은 bootstrap 테스트가 수행한다
compileOnly(platform(libs.springModulithBom))
compileOnly(libs.springModulithApi)

testImplementation(platform(libs.junitBom))
testImplementation(libs.junitJupiter)
testRuntimeOnly(libs.junitPlatformLauncher)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package kr.ac.kookmin.stream.event.domain.archive.domain;

import java.time.LocalDate;
import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;

@Getter
@EqualsAndHashCode
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Archive {

private Long id;
private String title;
private String content;
private LocalDate startDate;
private LocalDate endDate;
private String location;
private String departmentName;
private List<Long> imageIds;

public static Archive of(
Long id,
String title,
String content,
LocalDate startDate,
LocalDate endDate,
String location,
String departmentName,
List<Long> imageIds
) {
return new Archive(id, title, content, startDate, endDate, location, departmentName, imageIds);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package kr.ac.kookmin.stream.event.domain.archive.domain;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;

@Getter
@EqualsAndHashCode
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class ArchiveRelatedLink {

private Long id;
private Long archiveId;
private String title;
private String url;
private int displayOrder;

public static ArchiveRelatedLink of(
Long id,
Long archiveId,
String title,
String url,
int displayOrder
) {
return new ArchiveRelatedLink(id, archiveId, title, url, displayOrder);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package kr.ac.kookmin.stream.event.domain.event.domain;

import java.time.LocalDateTime;
import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;

@Getter
@EqualsAndHashCode
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Event {

private Long id;
private String title;
private String description;
private String target;
private String place;
private LocalDateTime eventStartAt;
private LocalDateTime eventEndAt;
private LocalDateTime applyStartAt;
private LocalDateTime applyEndAt;
private RecruitType recruitType;
private List<Long> imageIds;
private int capacity;
private RecruitStatus recruitStatus;
private Long createdBy;

public static Event of(
Long id,
String title,
String description,
String target,
String place,
LocalDateTime eventStartAt,
LocalDateTime eventEndAt,
LocalDateTime applyStartAt,
LocalDateTime applyEndAt,
RecruitType recruitType,
List<Long> imageIds,
int capacity,
RecruitStatus recruitStatus,
Long createdBy
) {
return new Event(
id, title, description, target, place, eventStartAt, eventEndAt, applyStartAt,
applyEndAt, recruitType, imageIds, capacity, recruitStatus, createdBy
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package kr.ac.kookmin.stream.event.domain.event.domain;

import java.time.LocalDateTime;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;

@Getter
@EqualsAndHashCode
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class EventApplication {

private Long id;
private Long eventId;
private Long memberId;
private EventApplicationStatus status;
private LocalDateTime appliedAt;
private LocalDateTime canceledAt;

public static EventApplication of(
Long id,
Long eventId,
Long memberId,
EventApplicationStatus status,
LocalDateTime appliedAt,
LocalDateTime canceledAt
) {
return new EventApplication(id, eventId, memberId, status, appliedAt, canceledAt);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package kr.ac.kookmin.stream.event.domain.event.domain;

import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;

@Getter
@EqualsAndHashCode
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class EventApplicationAnswer {

private Long id;
private Long eventApplicationId;
private Long eventQuestionId;
private String answerText;
private List<Integer> selectedOptions;

public static EventApplicationAnswer of(
Long id,
Long eventApplicationId,
Long eventQuestionId,
String answerText,
List<Integer> selectedOptions
) {
return new EventApplicationAnswer(id, eventApplicationId, eventQuestionId, answerText, selectedOptions);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package kr.ac.kookmin.stream.event.domain.event.domain;

public enum EventApplicationStatus {
APPLIED,
CANCELED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package kr.ac.kookmin.stream.event.domain.event.domain;

import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;

@Getter
@EqualsAndHashCode
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class EventQuestion {

private Long id;
private Long eventId;
private String questionText;
private QuestionType questionType;
private boolean required;
private int displayOrder;
private List<String> options;

public static EventQuestion of(
Long id,
Long eventId,
String questionText,
QuestionType questionType,
boolean required,
int displayOrder,
List<String> options
) {
return new EventQuestion(id, eventId, questionText, questionType, required, displayOrder, options);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package kr.ac.kookmin.stream.event.domain.event.domain;

public enum QuestionType {
SHORT_TEXT,
LONG_TEXT,
SINGLE_CHOICE,
MULTIPLE_CHOICE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package kr.ac.kookmin.stream.event.domain.event.domain;

public enum RecruitStatus {
BEFORE_OPEN,
OPEN,
CLOSED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package kr.ac.kookmin.stream.event.domain.event.domain;

public enum RecruitType {
FIRST_COME,
OPEN
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package kr.ac.kookmin.stream.event.domain.locker.domain;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;

@Getter
@EqualsAndHashCode
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Locker {

private Long id;
private String lockerNumber;
private String section;
private int rowNo;
private int columnNo;
private LockerStatus status;

public static Locker of(
Long id,
String lockerNumber,
String section,
int rowNo,
int columnNo,
LockerStatus status
) {
return new Locker(id, lockerNumber, section, rowNo, columnNo, status);
}
}
Loading