From 64605d3920a2e4a725f0ca0e8658de3c9595211b Mon Sep 17 00:00:00 2001 From: Phodal Huang Date: Sat, 24 Sep 2022 17:10:57 +0800 Subject: [PATCH] feat(fkl): add aggregate support --- crates/fkl_parser/src/mir/mod.rs | 4 ++ .../src/mir/strategy/bounded_context.rs | 8 +++- crates/fkl_parser/src/mir/tactic/aggregate.rs | 8 ++++ crates/fkl_parser/src/mir/tactic/entity.rs | 12 ++++- crates/fkl_parser/src/mir/tactic/service.rs | 4 ++ crates/fkl_parser/src/parser/ast.rs | 2 +- crates/fkl_parser/src/parser/fkl.pest | 12 ++--- crates/fkl_parser/src/parser/parser.rs | 18 +++++--- crates/fkl_parser/src/tests.rs | 46 +++++++++++++++++-- crates/fkl_parser/src/transform.rs | 35 +++++++++++--- 10 files changed, 119 insertions(+), 30 deletions(-) diff --git a/crates/fkl_parser/src/mir/mod.rs b/crates/fkl_parser/src/mir/mod.rs index 177bbf3..6387701 100644 --- a/crates/fkl_parser/src/mir/mod.rs +++ b/crates/fkl_parser/src/mir/mod.rs @@ -5,3 +5,7 @@ pub mod binding; pub use strategy::context_map::*; pub use strategy::domain::*; pub use strategy::bounded_context::*; +pub use tactic::aggregate::*; +pub use tactic::entity::*; +pub use tactic::value_object::*; +pub use tactic::domain_object::*; diff --git a/crates/fkl_parser/src/mir/strategy/bounded_context.rs b/crates/fkl_parser/src/mir/strategy/bounded_context.rs index d4c5bcb..861be05 100644 --- a/crates/fkl_parser/src/mir/strategy/bounded_context.rs +++ b/crates/fkl_parser/src/mir/strategy/bounded_context.rs @@ -1,6 +1,9 @@ +use std::fmt::{Display, Formatter}; + use serde::Deserialize; use serde::Serialize; -use std::fmt::{Display, Formatter}; + +use crate::mir::tactic::aggregate::Aggregate; // # Bounded Context // A description of a boundary (typically a subsystem, or the work of a particular team) within @@ -8,11 +11,12 @@ use std::fmt::{Display, Formatter}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)] pub struct BoundedContext { pub name: String, + pub aggregates: Vec, } impl BoundedContext { pub fn new(name: &str) -> Self { - BoundedContext { name: name.to_string() } + BoundedContext { name: name.to_string(), aggregates: vec![] } } } diff --git a/crates/fkl_parser/src/mir/tactic/aggregate.rs b/crates/fkl_parser/src/mir/tactic/aggregate.rs index dbb3c66..b421faf 100644 --- a/crates/fkl_parser/src/mir/tactic/aggregate.rs +++ b/crates/fkl_parser/src/mir/tactic/aggregate.rs @@ -1,5 +1,6 @@ use serde::Deserialize; use serde::Serialize; +use crate::mir::tactic::entity::Entity; // Cluster the entities and value objects into aggregates and define boundaries around each. // Choose one entity to be the root of each aggregate, and allow external objects to hold @@ -10,5 +11,12 @@ use serde::Serialize; pub struct Aggregate { pub name: String, pub description: String, + pub entities: Vec, +} + +impl Aggregate { + pub fn new(name: &str) -> Self { + Aggregate { name: name.to_string(), description: "".to_string(), entities: vec![] } + } } diff --git a/crates/fkl_parser/src/mir/tactic/entity.rs b/crates/fkl_parser/src/mir/tactic/entity.rs index 88a75b3..b7edc95 100644 --- a/crates/fkl_parser/src/mir/tactic/entity.rs +++ b/crates/fkl_parser/src/mir/tactic/entity.rs @@ -1,3 +1,13 @@ +use serde::Deserialize; +use serde::Serialize; + +use crate::mir::tactic::block::Field; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)] pub struct Entity { - pub name: String, + pub name: String, + pub description: String, + pub is_aggregate_root: bool, + pub identify: Field, + pub fields: Vec, } diff --git a/crates/fkl_parser/src/mir/tactic/service.rs b/crates/fkl_parser/src/mir/tactic/service.rs index ab8e79a..ea11676 100644 --- a/crates/fkl_parser/src/mir/tactic/service.rs +++ b/crates/fkl_parser/src/mir/tactic/service.rs @@ -1,3 +1,7 @@ +use serde::Deserialize; +use serde::Serialize; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)] pub struct Service { } diff --git a/crates/fkl_parser/src/parser/ast.rs b/crates/fkl_parser/src/parser/ast.rs index 50d0e52..1161ed9 100644 --- a/crates/fkl_parser/src/parser/ast.rs +++ b/crates/fkl_parser/src/parser/ast.rs @@ -73,6 +73,7 @@ pub struct ContextMapDecl { pub struct BoundedContextDecl { pub name: String, pub aggregates: Vec, + pub use_domain_objects: Vec, } #[derive(Debug, Clone, PartialEq, Eq, Default)] @@ -116,7 +117,6 @@ pub struct ApplicationServiceDecl { #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct AggregateDecl { pub name: String, - pub is_root: bool, pub inline_doc: String, pub used_domain_objects: Vec, pub entities: Vec, diff --git a/crates/fkl_parser/src/parser/fkl.pest b/crates/fkl_parser/src/parser/fkl.pest index 3ecdf78..e85b4f2 100644 --- a/crates/fkl_parser/src/parser/fkl.pest +++ b/crates/fkl_parser/src/parser/fkl.pest @@ -15,11 +15,11 @@ context_map_decl = { } context_decl = { - "Context" ~ identifier ~ "{" ~ (module_decl | aggregate_decl | inline_doc)* ~ "}" + "Context" ~ identifier ~ "{" ~ (module_decl | aggregate_decl | use_domain_object_decl | inline_doc)* ~ "}" } context_node_decl = { - "context" ~ identifier + ("Context" | "context") ~ identifier } context_node_rel = { @@ -63,7 +63,7 @@ aggregate_decl = { } use_domain_object_decl = { - ("Concept" | "Entity" | "VO" | "ValueObject" ) ~ identifier ~ ("," ~ identifier)* ~ ";" + ("Concept" | "Entity" | "VO" | "ValueObject" | "Aggregate" ) ~ identifier ~ ("," ~ identifier)* ~ ";" } entity_decl = { @@ -108,11 +108,7 @@ param_type = { } component_decl = { - "Component" ~ identifier ~ "{" ~ (attr_decl | inline_doc | use_aggregate )* ~ "}" -} - -use_aggregate = { - "Aggregate" ~ identifier ~ ";"? + "Component" ~ identifier ~ "{" ~ (attr_decl | inline_doc | use_domain_object_decl )* ~ "}" } attr_decl = { diff --git a/crates/fkl_parser/src/parser/parser.rs b/crates/fkl_parser/src/parser/parser.rs index 6411cc6..22254d8 100644 --- a/crates/fkl_parser/src/parser/parser.rs +++ b/crates/fkl_parser/src/parser/parser.rs @@ -85,6 +85,7 @@ fn consume_context_map(pair: Pair) -> ContextMapDecl { context_decl_map.insert(context_name.clone(), BoundedContextDecl { name: context_name, aggregates: vec![], + use_domain_objects: vec![], }); } Rule::rel_symbol => { @@ -176,6 +177,9 @@ fn consume_context(pair: Pair) -> BoundedContextDecl { Rule::aggregate_decl => { context.aggregates.push(consume_aggregate(p)); } + Rule::use_domain_object_decl => { + context.use_domain_objects.push(consume_use_domain_object(p)); + } _ => println!("unreachable context rule: {:?}", p.as_rule()) }; } @@ -405,16 +409,18 @@ Context ShoppingCarContext { assert_eq!(decls[0], FklDeclaration::ContextMap(ContextMapDecl { name: Identifier { name: "".to_string(), - loc: Default::default() + loc: Default::default(), }, contexts: vec![ BoundedContextDecl { name: "MallContext".to_string(), aggregates: vec![], + use_domain_objects: vec![], }, BoundedContextDecl { name: "ShoppingCarContext".to_string(), aggregates: vec![], + use_domain_objects: vec![], }, ], relations: vec![ @@ -436,7 +442,6 @@ just for test assert_eq!(decls[0], FklDeclaration::Aggregate(AggregateDecl { name: "Sample".to_string(), - is_root: false, inline_doc: r#" inline doc sample just for test "#.to_string(), @@ -458,7 +463,6 @@ Aggregate ShoppingCart { assert_eq!(decls[0], FklDeclaration::Aggregate(AggregateDecl { name: "ShoppingCart".to_string(), - is_root: false, inline_doc: "".to_string(), used_domain_objects: vec![], entities: vec![EntityDecl { @@ -640,7 +644,6 @@ Entity SalesPerson { aggregates: vec![ AggregateDecl { name: "Cart".to_string(), - is_root: false, inline_doc: "".to_string(), used_domain_objects: vec![], entities: vec![EntityDecl { @@ -690,6 +693,7 @@ Entity SalesPerson { value_objects: vec![], } ], + use_domain_objects: vec![], })); } @@ -728,11 +732,11 @@ Component SalesComponent { let except = FklDeclaration::ContextMap(ContextMapDecl { name: Identifier { name: "Mall".to_string(), - loc: Loc(11, 15) + loc: Loc(11, 15), }, contexts: vec![ - BoundedContextDecl { name: "OrderContext".to_string(), aggregates: vec![] }, - BoundedContextDecl { name: "SalesContext".to_string(), aggregates: vec![] }, + BoundedContextDecl { name: "OrderContext".to_string(), aggregates: vec![], use_domain_objects: vec![] }, + BoundedContextDecl { name: "SalesContext".to_string(), aggregates: vec![], use_domain_objects: vec![] }, ], relations: vec![ContextRelation { source: "SalesContext".to_string(), diff --git a/crates/fkl_parser/src/tests.rs b/crates/fkl_parser/src/tests.rs index e17260a..41f981b 100644 --- a/crates/fkl_parser/src/tests.rs +++ b/crates/fkl_parser/src/tests.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod test { use crate::mir; - use crate::mir::{BoundedContext, ContextRelation, ContextState}; + use crate::mir::{Aggregate, BoundedContext, ContextRelation, ContextState}; use crate::mir::ConnectionDirection::PositiveDirected; use crate::parse; @@ -14,6 +14,10 @@ ContextMap TicketBooking { Reservation -> User; } +Context Reservation { + Aggregate Reservation; +} + Aggregate Reservation { Entity Ticket, Reservation; } @@ -36,6 +40,10 @@ Entity Reservation { Entity Ticket {} +Context Cinema { + Aggregate Cinema; +} + Aggregate Cinema { Entity Cinema, ScreeningRoom, Seat; } @@ -44,6 +52,10 @@ Entity Cinema { } Entity ScreeningRoom { } Entity Seat { } +Context Movie { + Aggregate Movie; +} + Aggregate Movie { Entity Movie, Actor, Publisher; } @@ -52,6 +64,10 @@ Entity Movie { } Entity Actor { } Entity Publisher { } +Context User { + Aggregate User; +} + Aggregate User { Entity User; } @@ -86,10 +102,30 @@ ValueObject Notifications { } name: "TicketBooking".to_string(), state: ContextState::ToBe, contexts: vec![ - BoundedContext { name: "Cinema".to_string() }, - BoundedContext { name: "Movie".to_string() }, - BoundedContext { name: "Reservation".to_string() }, - BoundedContext { name: "User".to_string() }], + BoundedContext { + name: "Cinema".to_string(), + aggregates: vec![ + Aggregate { name: "Cinema".to_string(), description: "".to_string(), entities: vec![] } + ], + }, + BoundedContext { + name: "Movie".to_string(), + aggregates: vec![ + Aggregate { name: "Movie".to_string(), description: "".to_string(), entities: vec![] } + ], + }, + BoundedContext { + name: "Reservation".to_string(), + aggregates: vec![ + Aggregate { name: "Reservation".to_string(), description: "".to_string(), entities: vec![] } + ], + }, + BoundedContext { + name: "User".to_string(), + aggregates: vec![ + Aggregate { name: "User".to_string(), description: "".to_string(), entities: vec![] } + ], + }], relations: vec![ ContextRelation { source: "Reservation".to_string(), diff --git a/crates/fkl_parser/src/transform.rs b/crates/fkl_parser/src/transform.rs index 803c21f..ee3bd91 100644 --- a/crates/fkl_parser/src/transform.rs +++ b/crates/fkl_parser/src/transform.rs @@ -2,12 +2,14 @@ use indexmap::IndexMap; use crate::{ContextMap, mir, ParseError}; use crate::mir::{BoundedContext, ConnectionDirection, ContextRelationType}; +use crate::mir::tactic::aggregate::Aggregate; +use crate::parser::ast::{AggregateDecl, FklDeclaration, RelationDirection}; use crate::parser::parse as ast_parse; -use crate::parser::ast::{FklDeclaration, RelationDirection}; pub struct MirTransform { pub context_map_name: String, pub contexts: IndexMap, + pub aggregates: IndexMap, pub relations: Vec, } @@ -16,6 +18,7 @@ impl MirTransform { let mut transform = MirTransform { context_map_name: "".to_string(), contexts: Default::default(), + aggregates: Default::default(), relations: vec![], }; @@ -35,8 +38,8 @@ impl MirTransform { } fn lower_decls(&mut self, decls: Vec) { - decls.iter().for_each(|decl| { - match decl { + decls.iter().for_each(|declaration| { + match declaration { FklDeclaration::None => {} FklDeclaration::ContextMap(context_map) => { self.context_map_name = context_map.name.name.clone(); @@ -58,11 +61,17 @@ impl MirTransform { }); } FklDeclaration::BoundedContext(bc) => { - let bounded_context = mir::BoundedContext::new(&bc.name); + let mut bounded_context = mir::BoundedContext::new(&bc.name); + bc.use_domain_objects.iter().for_each(|domain_object| { + let aggregate = Aggregate::new(&domain_object.name); + bounded_context.aggregates.push(aggregate); + }); self.contexts.insert(bounded_context.name.clone(), bounded_context); } FklDeclaration::Domain(_) => {} - FklDeclaration::Aggregate(_) => {} + FklDeclaration::Aggregate(decl) => { + self.transform_aggregate(&decl); + } FklDeclaration::DomainService(_) => {} FklDeclaration::ApplicationService(_) => {} FklDeclaration::Entity(_) => {} @@ -71,6 +80,20 @@ impl MirTransform { } }); } + + fn transform_aggregate(&mut self, decl: &AggregateDecl) -> mir::Aggregate { + let mut aggregate = mir::Aggregate::new(&decl.name); + self.aggregates.insert(aggregate.name.clone(), aggregate.clone()); + // decl.entities.iter().for_each(|entity| { + // }); + + aggregate + } + + // fn transform_entity(decl: &EntityDecl) -> mir::Entity { + // let entity = mir::Entity::new(&decl.name); + // entity + // } } fn transform_connection(rd: &RelationDirection) -> ConnectionDirection { @@ -84,8 +107,8 @@ fn transform_connection(rd: &RelationDirection) -> ConnectionDirection { #[cfg(test)] mod tests { - use crate::mir::ConnectionDirection::PositiveDirected; use crate::mir::{ContextRelation, ContextRelationType}; + use crate::mir::ConnectionDirection::PositiveDirected; use crate::transform::MirTransform; #[test]