ๆ็ตๆดๆฐ: 2026ๅนด1ๆ
- ๆฆ่ฆ
- ใฌใคใคใผๆง้
- ใใผใฟใใผใน่จญ่จ
- gRPC/ในใญใผใ่จญ่จ
- ๅฝๅ่ฆๅ
- ใญใฃใใทใฅๆฆ็ฅ
- ใใผใฟใใญใผ
- ใใฃใฌใฏใใชๆง้
StationAPI ใฏๆฅๆฌใฎ้้้ง ๆ ๅ ฑใๆไพใใ gRPC API ใงใใใฏใชใผใณใขใผใญใใฏใใฃใซๅบใฅใใ4ๅฑคๆง้ ใๆก็จใใใใธใในใญใธใใฏใจๆ่ก็้ขๅฟไบใๆ็ขบใซๅ้ขใใฆใใพใใ
| ้ ็ฎ | ๆ่ก |
|---|---|
| ่จ่ช | Rust (Edition 2021) |
| ใฉใณใฟใคใ | tokio |
| ใใผใฟใใผใน | PostgreSQL 15+ |
| ORM | sqlx (ใณใณใใคใซๆใฏใจใชๆค่จผ) |
| API | gRPC (tonic) |
| ใทใชใขใฉใคใบ | Protocol Buffers |
StationAPI ใฏ4ใคใฎๅฑคใงๆงๆใใใฆใใพใใๅๅฑคใฏไพๅญๆงใฎๆนๅใๅ ๅด๏ผDomain๏ผใซๅใใใใ่จญ่จใใใฆใใพใใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Presentation ๅฑค โ
โ (gRPC Controller, ใจใฉใผใใณใใชใณใฐ) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ UseCase ๅฑค โ
โ (Interactor, DTO, ใใธใในใญใธใใฏ) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Infrastructure ๅฑค โ
โ (Repositoryๅฎ่ฃ
, Rowๆง้ ไฝ, DBๆฅ็ถ) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Domain ๅฑค โ
โ (Entity, Repository Interface, ใใธใในใซใผใซ) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ่ฒฌๅ: ใณใขใใธใในใญใธใใฏใจใใผใฟใขใใซใฎๅฎ็พฉ
| ใใฃใฌใฏใใช/ใใกใคใซ | ๅ ๅฎน |
|---|---|
entity/ |
ใใกใคใณใจใณใใฃใใฃ๏ผStation, Line, TrainType, Company ใชใฉ๏ผ |
repository/ |
ใชใใธใใชใคใณใฟใผใใงใผใน๏ผasync_trait ใไฝฟ็จ๏ผ |
normalize.rs |
ใใญในใๆญฃ่ฆๅ๏ผใฒใใใชโใซใฟใซใใๅ จ่งโๅ่งๅคๆ๏ผ |
error.rs |
ใใกใคใณใจใฉใผๅ๏ผNotFound, InfrastructureError, Unexpected๏ผ |
่จญ่จๅๅ:
- ๅค้จไพๅญใๆใใชใ็ด็ฒใช Rust ใณใผใ
- ใชใใธใใชใฏ trait ใจใใฆๅฎ็พฉใใๅฎ่ฃ ใ Infrastructure ๅฑคใซๅง่ญฒ
- ๅค่จ่ชๅฏพๅฟ๏ผๆฅๆฌ่ชใใซใฟใซใใใญใผใๅญใไธญๅฝ่ชใ้ๅฝ่ช๏ผ
่ฒฌๅ: ใขใใชใฑใผใทใงใณใใธใในใญใธใใฏใจใใผใฟๅคๆ
| ใใฃใฌใฏใใช/ใใกใคใซ | ๅ ๅฎน |
|---|---|
interactor/query.rs |
QueryInteractor - ไธป่ฆใชใฆใผในใฑใผในๅฎ่ฃ
๏ผ็ด950่ก๏ผ |
traits/query.rs |
QueryUseCase ใใฌใคใๅฎ็พฉ๏ผ20ไปฅไธใฎ้ๅๆใกใฝใใ๏ผ |
dto/ |
ใใผใฟๅคๆใชใใธใงใฏใ๏ผEntity โ gRPC ใกใใปใผใธ๏ผ |
error.rs |
ใฆใผในใฑใผในใจใฉใผๅ |
้่ฆใชใกใฝใใ:
// update_station_vec_with_attributes (query.rs:169-265)
// - ้ง
ใใผใฟใซใฉใคใณใไผ็คพใๅ่ป็จฎๅฅใไปๅ
// - N+1ๅ้กใๅ้ฟใใใใใใฏใจใช่จญ่จ
async fn update_station_vec_with_attributes(
&self,
mut stations: Vec<Station>,
line_group_id: Option<u32>,
) -> Result<Vec<Station>, UseCaseError>่ฒฌๅ: ใใผใฟๆฐธ็ถๅใจๅค้จใทในใใ ้ฃๆบ
| ใใกใคใซ | ๅ ๅฎน |
|---|---|
station_repository.rs |
StationRow + MyStationRepository ๅฎ่ฃ
|
line_repository.rs |
LineRow + MyLineRepository ๅฎ่ฃ
|
train_type_repository.rs |
TrainTypeRow + MyTrainTypeRepository ๅฎ่ฃ
|
company_repository.rs |
CompanyRow + MyCompanyRepository ๅฎ่ฃ
|
่จญ่จใใฟใผใณ:
- ๅ Repository ใฏ
Arc<Pool<Postgres>>ใใฉใใ Internal*Repositoryๆง้ ไฝใซๅฎ้ใฎ SQL ๅฎ่กใๅง่ญฒ#[derive(sqlx::FromRow)]ใซใใๅๅฎๅ จใช Row ใใใใณใฐ
่ฒฌๅ: ๅค้จ API ใฎๅ ฌ้ใจใชใฏใจในใ/ใฌในใใณในใใณใใชใณใฐ
| ใใกใคใซ | ๅ ๅฎน |
|---|---|
controller/grpc.rs |
MyApi - 14ใฎ gRPC ใจใณใใใคใณใๅฎ่ฃ
|
error.rs |
PresentationalError ใจ tonic::Status ใธใฎๅคๆ |
ใในใฆใฎใใผใใซใฏ UNLOGGED ใจใใฆไฝๆใใใใใฉใผใใณในใๅชๅ
ใใฆใใพใใ
| ใใผใใซ | ไธปใญใผ | ๆฆ่ฆ |
|---|---|---|
companies |
company_cd | ้้ไผ็คพๆ ๅ ฑ |
lines |
line_cd | ่ทฏ็ทๆ ๅ ฑ |
stations |
station_cd | ้ง ๆ ๅ ฑ |
types |
id | ๅ่ป็จฎๅฅ |
station_station_types |
id | ้ง ใจๅ่ป็จฎๅฅใฎ้ข้ฃ |
line_aliases |
id | ่ทฏ็ทใจใคใชใขใน |
connections |
- | ้ง ้ๆฅ็ถ |
aliases |
- | ๆค็ดข็จใจใคใชใขใน |
-- ไฝฟ็จใใฆใใ PostgreSQL ๆกๅผต
CREATE EXTENSION IF NOT EXISTS pg_trgm; -- ใใฉใคใฐใฉใ ๆค็ดข
CREATE EXTENSION IF NOT EXISTS btree_gist; -- GiST ใคใณใใใฏใน
-- ไธป่ฆใคใณใใใฏใน
CREATE INDEX idx_stations_station_g_cd ON stations(station_g_cd);
CREATE INDEX idx_stations_line_cd ON stations(line_cd);
CREATE INDEX idx_performance_station_name_trgm ON stations
USING gin(station_name gin_trgm_ops); -- ใใใพใๆค็ดข็จ- ใใคใฐใฌใผใทใงใณ:
data/create_table.sqlใๆดๆฐ - Row ๆง้ ไฝ: ๅฏพๅฟใใ
*Rowๆง้ ไฝใ Infrastructure ๅฑคใงๆดๆฐ - Entity: ๅฟ ่ฆใซๅฟใใฆ Domain ๅฑคใฎ Entity ใๆดๆฐ
- ๅคๆใญใธใใฏ:
impl From<XxxRow> for Xxxใๆดๆฐ - DTO: gRPC ใกใใปใผใธใธใฎๅคๆใ
use_case/dto/ใงๆดๆฐ
stationapi.proto ใง14ใฎใจใณใใใคใณใใๅฎ็พฉ:
| ใซใใดใช | ใกใฝใใ |
|---|---|
| ้ง ๆค็ดข | GetStationById, GetStationByIdList, GetStationsByGroupId, GetStationsByCoordinates, GetStationsByLineId, GetStationsByName, GetStationsByLineGroupId |
| ่ทฏ็ทๆค็ดข | GetLineById, GetLinesByIdList, GetLinesByName |
| ็ต่ทฏๆค็ดข | GetRoutes, GetRoutesMinimal, GetConnectedRoutes |
| ๅ่ป็จฎๅฅ | GetTrainTypesByStationId, GetRouteTypes |
- ๅพๆนไบๆๆง: ๆฐใใฃใผใซใใซใฏ
optionalใญใผใฏใผใใไฝฟ็จ - ใใซใ่จญๅฎ:
build.rsใงserdeใใฌใคใใ่ฟฝๅ - DTO ๆดๆฐ:
src/use_case/dto/*.rsใฎใใใใณใฐใๆดๆฐ - ใในใๆดๆฐ: ๆฐใใฃใผใซใใฎ็ตฑๅใในใใ่ฟฝๅ
// ๅพๆนไบๆๆงใฎใใ่ฟฝๅ ไพ
message Station {
// ๆขๅญใใฃใผใซใ...
optional string new_field = 25; // optional ใง่ฟฝๅ
}| ็จฎๅฅ | ๅ ดๆ | ็ฎ็ | ็นๅพด |
|---|---|---|---|
| Row | infrastructure/*.rs |
DB่กใฎ็ดๆฅใใใใณใฐ | #[derive(sqlx::FromRow)]ใDBใซใฉใ ๅใจไธ่ด |
| Entity | domain/entity/*.rs |
ใใกใคใณใขใใซ | ใใธใในใญใธใใฏใใในใๆง้ ใๅค่จ่ชๅฏพๅฟ |
// infrastructure/station_repository.rs
#[derive(sqlx::FromRow, Clone)]
pub struct StationRow {
pub station_cd: i32, // DBใซใฉใ ๅใจไธ่ด
pub station_g_cd: i32,
pub station_name: String,
pub line_cd: i32,
// ... ็ด19ใใฃใผใซใ
}็นๅพด:
- ใใฃใผใซใๅใฏ PostgreSQL ใซใฉใ ๅใจๅฎๅ จไธ่ด๏ผsnake_case๏ผ
- ใใผใฟใใผในใใคใใฃใๅใไฝฟ็จ:
i32,i64,f64,Option<T>,String - ใญใธใใฏใๆใใชใ็ด็ฒใชใใผใฟใใซใใผ
// domain/entity/station.rs
pub struct Station {
pub station_cd: u32, // ใใธใในๅ๏ผ็ฌฆๅทใชใ๏ผ
pub station_g_cd: u32,
pub station_name: String,
pub line: Option<Box<Line>>, // ใในใๆง้
pub lines: Vec<Line>, // ใณใฌใฏใทใงใณ
pub train_type: Option<Box<TrainType>>,
pub station_numbers: Vec<StationNumber>,
// ... ็ด66ใใฃใผใซใ
}็นๅพด:
- ใใธใในใปใใณใใฃใฏในใๅๆ ใใๅ๏ผไพ:
StopConditionๅๆๅ๏ผ - ใในใๆง้ ใๅซใ๏ผ
Option<Box<Line>>,Vec<Line>ใชใฉ๏ผ - ๅค่จ่ชๅใใตใใผใ:
station_name_r๏ผใญใผใๅญ๏ผ,station_name_zh๏ผไธญๅฝ่ช๏ผ,station_name_ko๏ผ้ๅฝ่ช๏ผ Clone,Debug,Serialize,Deserialize,PartialEqใๅฎ่ฃ
Database (PostgreSQL)
โ
Row (sqlx::FromRow) โ ็ดๆฅใใใใณใฐ: StationRow
โ
Entity (From<Row>) โ ๅๅคๆใNoneๅๆๅ: Station
โ
Enriched Entity โ UseCaseๅฑคใงใในใใใผใฟ่ฟฝๅ
โ
gRPC Message โ Protoๅคๆ: proto::Station
โ
Network ResponseStationAPI ใฏ็พๆ็นใงๆ็คบ็ใชใคใณใกใขใชใญใฃใใทใฅใๅฎ่ฃ ใใฆใใพใใใใใฎไปฃใใใไปฅไธใฎๆ้ฉๅๆฆ็ฅใๆก็จใใฆใใพใใ
query.rs:169-265 ใฎ update_station_vec_with_attributes ใกใฝใใใงใฏใN+1ๅ้กใๅ้ฟใใใใใซใใใใฏใจใชใไฝฟ็จใใฆใใพใใ
// 1. ใในใฆใฎ station_g_cd ใๆฝๅบ
let station_group_ids = stations.iter()
.map(|s| s.station_g_cd as u32)
.collect::<Vec<u32>>();
// 2. ไธๆฌใฏใจใชใง้ข้ฃใใผใฟใๅๅพ๏ผN+1ๅ้ฟ๏ผ
let stations_by_group_ids = self
.get_stations_by_group_id_vec(&station_group_ids).await?;
let lines = self
.get_lines_by_station_group_id_vec(&station_group_ids).await?;
let train_types = self
.get_train_types_by_station_id_vec(&station_ids, line_group_id).await?;
// 3. ใกใขใชไธใง้ข้ฃไปใ๏ผO(1)ใฏใจใช/ใจใณใชใใใกใณใ๏ผ็ตๆ: ใจใณใชใใใกใณใๅฆ็ใใใO(1)ใฏใจใช๏ผN้ง ใซๅฏพใใฆNๅใฎใฏใจใชใงใฏใชใ๏ผ
query.rs:223 ไป่ฟใงใคใณใกใขใช้่คๆ้คใๅฎๆฝ:
let mut seen_line_cds = std::collections::HashSet::new();
let lines: Vec<Line> = lines
.iter()
.filter(|&l| {
l.station_g_cd.unwrap_or(0) == station.station_g_cd
&& seen_line_cds.insert(l.line_cd) // HashSetใง้่ค้ฒๆญข
})
.cloned()
.collect();- ใใผใฟ่ฆๆจก: ๆฅๆฌใฎ้้ใใผใฟใฏๆฏ่ผ็ๅฐ่ฆๆจก๏ผ็ด9,000้ง ๏ผ
- ๆดๆฐ้ ปๅบฆ: CSV ใคใณใใผใใซใใใใผใฟๆดๆฐใๅๆ
- ในใใผใใฌใน่จญ่จ: ๅใชใฏใจในใใฏ็ฌ็ซใใฆๅฆ็
- PostgreSQL ใฎๆ้ฉๅ: ใคใณใใใฏในใจใฏใจใชใใฉใณใใผใซใใๅน็ๅ
ๅคง่ฆๆจกๅใ้ซ้ ปๅบฆใขใฏใปในใๅฟ ่ฆใชๅ ดๅ:
mokaใlruใฏใฌใผใใซใใๆ็ใคใณใกใขใชใญใฃใใทใฅ- CSV ใคใณใใผใๆใฎใญใฃใใทใฅ็กๅนๅ
station_g_cdๅไฝใฎใฟใฐใใผใน็กๅนๅ
[Client]
โ
โผ gRPC Request
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Presentation ๅฑค (grpc.rs) โ
โ โโ MyApi::get_stations_by_id() โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ QueryUseCase ใกใฝใใๅผใณๅบใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ UseCase ๅฑค (query.rs) โ
โ โโ QueryInteractor::get_station_by_id() โ
โ โโ update_station_vec_with_attributes() โ
โ โโ ้ง
ใฐใซใผใไธๆฌๅๅพ โ
โ โโ ่ทฏ็ทไธๆฌๅๅพ โ
โ โโ ไผ็คพไธๆฌๅๅพ โ
โ โโ ๅ่ป็จฎๅฅไธๆฌๅๅพ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ Repository ใกใฝใใๅผใณๅบใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Infrastructure ๅฑค (station_repository.rs) โ
โ โโ MyStationRepository::find_by_id() โ
โ โโ SQL ใฏใจใชๅฎ่ก (sqlx) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ Row โ Entity ๅคๆ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Domain ๅฑค (entity/station.rs) โ
โ โโ impl From<StationRow> for Station โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ Entity โ gRPC Message ๅคๆ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ UseCase ๅฑค (dto/station.rs) โ
โ โโ impl From<Station> for proto::Station โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ gRPC Response
[Client]DomainError (sqlx ใจใฉใผ็ญ)
โ ?ๆผ็ฎๅญ
UseCaseError (ใฆใผในใฑใผในๅฑค)
โ From ใใฌใคใ
PresentationalError (ใใฌใผใณใใผใทใงใณๅฑค)
โ Into ใใฌใคใ
tonic::Status (gRPC ใฏใคใคใผใใฉใผใใใ)stationapi/src/
โโโ domain/ # ใณใขใใธใในใญใธใใฏ
โ โโโ entity/ # ใใกใคใณใจใณใใฃใใฃ
โ โ โโโ station.rs # Station (66ใใฃใผใซใ)
โ โ โโโ line.rs # Line (40ใใฃใผใซใ)
โ โ โโโ train_type.rs # TrainType
โ โ โโโ company.rs # Company
โ โ โโโ line_symbol.rs # LineSymbol
โ โ โโโ station_number.rs # StationNumber
โ โโโ repository/ # ๆฝ่ฑกใคใณใฟใผใใงใผใน
โ โ โโโ station_repository.rs
โ โ โโโ line_repository.rs
โ โ โโโ train_type_repository.rs
โ โ โโโ company_repository.rs
โ โโโ normalize.rs # ใใญในใๆญฃ่ฆๅ
โ โโโ error.rs # DomainError
โ
โโโ use_case/ # ใขใใชใฑใผใทใงใณใญใธใใฏ
โ โโโ interactor/
โ โ โโโ query.rs # QueryInteractor (็ด950่ก)
โ โโโ traits/
โ โ โโโ query.rs # QueryUseCase ใใฌใคใ
โ โโโ dto/ # ใใผใฟๅคๆ
โ โ โโโ station.rs
โ โ โโโ line.rs
โ โ โโโ train_type.rs
โ โ โโโ company.rs
โ โโโ error.rs # UseCaseError
โ
โโโ infrastructure/ # ใใผใฟๆฐธ็ถๅ
โ โโโ station_repository.rs # StationRow + MyStationRepository
โ โโโ line_repository.rs # LineRow + MyLineRepository
โ โโโ train_type_repository.rs # TrainTypeRow + MyTrainTypeRepository
โ โโโ company_repository.rs # CompanyRow + MyCompanyRepository
โ โโโ error.rs # InfrastructureError
โ
โโโ presentation/ # ๅค้จAPI
โ โโโ controller/
โ โ โโโ grpc.rs # MyApi (14ใจใณใใใคใณใ)
โ โโโ error.rs # PresentationalError
โ
โโโ lib.rs # ใขใธใฅใผใซๅฎฃ่จ
โโโ main.rs # ใจใณใใชใผใใคใณใ