1use std::time::Duration;
7
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
12#[serde(rename_all = "snake_case")]
13pub enum Archetype {
14 #[default]
16 Basic,
17 Gateway,
19 EventSourced,
21 Consumer,
23 Producer,
25 Bff,
27 Scheduled,
29 WebSocketGateway,
31 SagaOrchestrator,
33 AntiCorruptionLayer,
35}
36
37impl std::fmt::Display for Archetype {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 match self {
40 Self::Basic => write!(f, "basic"),
41 Self::Gateway => write!(f, "gateway"),
42 Self::EventSourced => write!(f, "event-sourced"),
43 Self::Consumer => write!(f, "consumer"),
44 Self::Producer => write!(f, "producer"),
45 Self::Bff => write!(f, "bff"),
46 Self::Scheduled => write!(f, "scheduled"),
47 Self::WebSocketGateway => write!(f, "websocket-gateway"),
48 Self::SagaOrchestrator => write!(f, "saga-orchestrator"),
49 Self::AntiCorruptionLayer => write!(f, "anti-corruption-layer"),
50 }
51 }
52}
53
54impl std::str::FromStr for Archetype {
55 type Err = String;
56
57 fn from_str(s: &str) -> Result<Self, Self::Err> {
58 match s.to_lowercase().as_str() {
59 "basic" => Ok(Self::Basic),
60 "gateway" => Ok(Self::Gateway),
61 "event-sourced" | "eventsourced" | "cqrs" => Ok(Self::EventSourced),
62 "consumer" => Ok(Self::Consumer),
63 "producer" => Ok(Self::Producer),
64 "bff" | "backend-for-frontend" => Ok(Self::Bff),
65 "scheduled" | "cron" | "jobs" => Ok(Self::Scheduled),
66 "websocket-gateway" | "websocket" | "ws" => Ok(Self::WebSocketGateway),
67 "saga-orchestrator" | "saga" => Ok(Self::SagaOrchestrator),
68 "anti-corruption-layer" | "acl" | "legacy-adapter" => Ok(Self::AntiCorruptionLayer),
69 _ => Err(format!("Unknown archetype: {}", s)),
70 }
71 }
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
76#[serde(rename_all = "lowercase")]
77pub enum Protocol {
78 Rest,
80 Graphql,
82 Grpc,
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
88#[serde(rename_all = "snake_case")]
89pub enum AuthMethod {
90 #[default]
92 None,
93 ApiKey,
95 HmacSha256,
97 HmacSha512Base64,
99 OAuth2,
101 Jwt,
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
107#[serde(rename_all = "lowercase")]
108pub enum CacheBackend {
109 #[default]
111 Memory,
112 Redis,
114 None,
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
120#[serde(rename_all = "lowercase")]
121pub enum MessageBroker {
122 #[default]
124 Kafka,
125 RabbitMq,
127 Redis,
129 Sqs,
131 PubSub,
133}
134
135impl std::fmt::Display for MessageBroker {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 match self {
138 Self::Kafka => write!(f, "kafka"),
139 Self::RabbitMq => write!(f, "rabbitmq"),
140 Self::Redis => write!(f, "redis"),
141 Self::Sqs => write!(f, "sqs"),
142 Self::PubSub => write!(f, "pubsub"),
143 }
144 }
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct ProjectConfig {
150 pub name: String,
152 #[serde(default)]
154 pub archetype: Archetype,
155 #[serde(default)]
157 pub protocols: Vec<Protocol>,
158 #[serde(default = "default_true")]
160 pub tracing: bool,
161 #[serde(default = "default_true")]
163 pub metrics: bool,
164 #[serde(default)]
166 pub gateway: Option<GatewayConfig>,
167 #[serde(default)]
169 pub consumer: Option<ConsumerConfig>,
170 #[serde(default)]
172 pub producer: Option<ProducerConfig>,
173 #[serde(default)]
175 pub bff: Option<BffConfig>,
176 #[serde(default)]
178 pub scheduled: Option<ScheduledConfig>,
179 #[serde(default)]
181 pub websocket_gateway: Option<WebSocketGatewayConfig>,
182 #[serde(default)]
184 pub saga_orchestrator: Option<SagaOrchestratorConfig>,
185 #[serde(default)]
187 pub acl: Option<AntiCorruptionLayerConfig>,
188}
189
190fn default_true() -> bool {
191 true
192}
193
194impl Default for ProjectConfig {
195 fn default() -> Self {
196 Self {
197 name: String::new(),
198 archetype: Archetype::default(),
199 protocols: vec![Protocol::Grpc],
200 tracing: true,
201 metrics: true,
202 gateway: None,
203 consumer: None,
204 producer: None,
205 bff: None,
206 scheduled: None,
207 websocket_gateway: None,
208 saga_orchestrator: None,
209 acl: None,
210 }
211 }
212}
213
214impl ProjectConfig {
215 pub fn new(name: impl Into<String>) -> Self {
217 Self {
218 name: name.into(),
219 ..Default::default()
220 }
221 }
222
223 pub fn with_archetype(mut self, archetype: Archetype) -> Self {
225 self.archetype = archetype;
226 match archetype {
227 Archetype::Gateway if self.gateway.is_none() => {
228 self.gateway = Some(GatewayConfig::default());
229 }
230 Archetype::Consumer if self.consumer.is_none() => {
231 self.consumer = Some(ConsumerConfig::default());
232 }
233 Archetype::Producer if self.producer.is_none() => {
234 self.producer = Some(ProducerConfig::default());
235 }
236 Archetype::Bff if self.bff.is_none() => {
237 self.bff = Some(BffConfig::default());
238 }
239 Archetype::Scheduled if self.scheduled.is_none() => {
240 self.scheduled = Some(ScheduledConfig::default());
241 }
242 Archetype::WebSocketGateway if self.websocket_gateway.is_none() => {
243 self.websocket_gateway = Some(WebSocketGatewayConfig::default());
244 }
245 Archetype::SagaOrchestrator if self.saga_orchestrator.is_none() => {
246 self.saga_orchestrator = Some(SagaOrchestratorConfig::default());
247 }
248 Archetype::AntiCorruptionLayer if self.acl.is_none() => {
249 self.acl = Some(AntiCorruptionLayerConfig::default());
250 }
251 _ => {}
252 }
253 self
254 }
255
256 pub fn with_protocols(mut self, protocols: Vec<Protocol>) -> Self {
258 self.protocols = protocols;
259 self
260 }
261
262 pub fn with_gateway(mut self, gateway: GatewayConfig) -> Self {
264 self.gateway = Some(gateway);
265 self
266 }
267
268 pub fn with_consumer(mut self, consumer: ConsumerConfig) -> Self {
270 self.consumer = Some(consumer);
271 self
272 }
273
274 pub fn with_producer(mut self, producer: ProducerConfig) -> Self {
276 self.producer = Some(producer);
277 self
278 }
279
280 pub fn with_bff(mut self, bff: BffConfig) -> Self {
282 self.bff = Some(bff);
283 self
284 }
285
286 pub fn with_scheduled(mut self, scheduled: ScheduledConfig) -> Self {
288 self.scheduled = Some(scheduled);
289 self
290 }
291
292 pub fn with_websocket_gateway(mut self, ws: WebSocketGatewayConfig) -> Self {
294 self.websocket_gateway = Some(ws);
295 self
296 }
297
298 pub fn with_saga_orchestrator(mut self, saga: SagaOrchestratorConfig) -> Self {
300 self.saga_orchestrator = Some(saga);
301 self
302 }
303
304 pub fn with_acl(mut self, acl: AntiCorruptionLayerConfig) -> Self {
306 self.acl = Some(acl);
307 self
308 }
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct GatewayConfig {
314 pub service_name: String,
316 pub display_name: String,
318 pub api_base_url: String,
320 #[serde(default)]
322 pub auth_method: AuthMethod,
323 #[serde(default)]
325 pub rate_limit: RateLimitConfig,
326 #[serde(default)]
328 pub cache: CacheConfig,
329 #[serde(default)]
331 pub server: ServerConfig,
332 #[serde(default)]
334 pub entities: Vec<EntityConfig>,
335 #[serde(default)]
337 pub endpoints: Vec<EndpointConfig>,
338}
339
340impl Default for GatewayConfig {
341 fn default() -> Self {
342 Self {
343 service_name: "exchange".to_string(),
344 display_name: "Exchange Gateway".to_string(),
345 api_base_url: "https://api.example.com".to_string(),
346 auth_method: AuthMethod::default(),
347 rate_limit: RateLimitConfig::default(),
348 cache: CacheConfig::default(),
349 server: ServerConfig::default(),
350 entities: vec![],
351 endpoints: vec![],
352 }
353 }
354}
355
356#[derive(Debug, Clone, Serialize, Deserialize)]
358pub struct RateLimitConfig {
359 pub public_rps: u32,
361 pub private_rps: u32,
363 pub burst: u32,
365}
366
367impl Default for RateLimitConfig {
368 fn default() -> Self {
369 Self {
370 public_rps: 10,
371 private_rps: 2,
372 burst: 5,
373 }
374 }
375}
376
377#[derive(Debug, Clone, Serialize, Deserialize)]
379pub struct CacheConfig {
380 #[serde(default = "default_true")]
382 pub enabled: bool,
383 #[serde(default)]
385 pub backend: CacheBackend,
386 #[serde(default = "default_public_ttl")]
388 pub public_ttl_secs: u64,
389 #[serde(default = "default_private_ttl")]
391 pub private_ttl_secs: u64,
392}
393
394fn default_public_ttl() -> u64 {
395 300
396}
397
398fn default_private_ttl() -> u64 {
399 60
400}
401
402impl Default for CacheConfig {
403 fn default() -> Self {
404 Self {
405 enabled: true,
406 backend: CacheBackend::Memory,
407 public_ttl_secs: 300,
408 private_ttl_secs: 60,
409 }
410 }
411}
412
413impl CacheConfig {
414 pub fn public_ttl(&self) -> Duration {
416 Duration::from_secs(self.public_ttl_secs)
417 }
418
419 pub fn private_ttl(&self) -> Duration {
421 Duration::from_secs(self.private_ttl_secs)
422 }
423}
424
425#[derive(Debug, Clone, Serialize, Deserialize)]
427pub struct ServerConfig {
428 #[serde(default = "default_http_port")]
430 pub http_port: u16,
431 #[serde(default = "default_grpc_port")]
433 pub grpc_port: u16,
434 #[serde(default = "default_health_port")]
436 pub health_port: u16,
437 #[serde(default = "default_metrics_port")]
439 pub metrics_port: u16,
440}
441
442fn default_http_port() -> u16 {
443 8080
444}
445
446fn default_grpc_port() -> u16 {
447 50051
448}
449
450fn default_health_port() -> u16 {
451 8081
452}
453
454fn default_metrics_port() -> u16 {
455 9090
456}
457
458impl Default for ServerConfig {
459 fn default() -> Self {
460 Self {
461 http_port: 8080,
462 grpc_port: 50051,
463 health_port: 8081,
464 metrics_port: 9090,
465 }
466 }
467}
468
469#[derive(Debug, Clone, Serialize, Deserialize)]
471pub struct EntityConfig {
472 pub name: String,
474 pub fields: Vec<FieldConfig>,
476}
477
478#[derive(Debug, Clone, Serialize, Deserialize)]
480pub struct FieldConfig {
481 pub name: String,
483 pub field_type: String,
485 #[serde(default)]
487 pub optional: bool,
488}
489
490#[derive(Debug, Clone, Serialize, Deserialize)]
492pub struct EndpointConfig {
493 pub name: String,
495 pub method: String,
497 pub path: String,
499 #[serde(default)]
501 pub private: bool,
502 #[serde(default)]
504 pub params: Vec<ParamConfig>,
505 pub response_type: String,
507 #[serde(default)]
509 pub cacheable: bool,
510}
511
512#[derive(Debug, Clone, Serialize, Deserialize)]
514pub struct ParamConfig {
515 pub name: String,
517 pub param_type: String,
519 #[serde(default = "default_true")]
521 pub required: bool,
522}
523
524#[derive(Debug, Clone, Serialize, Deserialize)]
526pub struct ConsumerConfig {
527 pub service_name: String,
529 pub display_name: String,
531 #[serde(default)]
533 pub broker: MessageBroker,
534 pub topics: Vec<TopicConfig>,
536 pub group_id: String,
538 #[serde(default)]
540 pub dlq: DlqConfig,
541 #[serde(default)]
543 pub retry: RetryConfig,
544 #[serde(default)]
546 pub idempotency: IdempotencyConfig,
547 #[serde(default)]
549 pub server: ServerConfig,
550}
551
552impl Default for ConsumerConfig {
553 fn default() -> Self {
554 Self {
555 service_name: "consumer".to_string(),
556 display_name: "Event Consumer".to_string(),
557 broker: MessageBroker::default(),
558 topics: vec![TopicConfig::default()],
559 group_id: "consumer-group".to_string(),
560 dlq: DlqConfig::default(),
561 retry: RetryConfig::default(),
562 idempotency: IdempotencyConfig::default(),
563 server: ServerConfig::default(),
564 }
565 }
566}
567
568#[derive(Debug, Clone, Serialize, Deserialize)]
570pub struct TopicConfig {
571 pub name: String,
573 pub event_type: String,
575 #[serde(default = "default_partitions")]
577 pub partitions: u32,
578 #[serde(default)]
580 pub auto_commit: bool,
581}
582
583fn default_partitions() -> u32 {
584 1
585}
586
587impl Default for TopicConfig {
588 fn default() -> Self {
589 Self {
590 name: "events".to_string(),
591 event_type: "Event".to_string(),
592 partitions: 1,
593 auto_commit: false,
594 }
595 }
596}
597
598#[derive(Debug, Clone, Serialize, Deserialize)]
600pub struct DlqConfig {
601 #[serde(default = "default_true")]
603 pub enabled: bool,
604 #[serde(default = "default_dlq_suffix")]
606 pub suffix: String,
607 #[serde(default = "default_max_retries")]
609 pub max_retries: u32,
610}
611
612fn default_dlq_suffix() -> String {
613 ".dlq".to_string()
614}
615
616fn default_max_retries() -> u32 {
617 3
618}
619
620impl Default for DlqConfig {
621 fn default() -> Self {
622 Self {
623 enabled: true,
624 suffix: ".dlq".to_string(),
625 max_retries: 3,
626 }
627 }
628}
629
630#[derive(Debug, Clone, Serialize, Deserialize)]
632pub struct RetryConfig {
633 #[serde(default = "default_max_retries")]
635 pub max_attempts: u32,
636 #[serde(default = "default_initial_backoff")]
638 pub initial_backoff_ms: u64,
639 #[serde(default = "default_max_backoff")]
641 pub max_backoff_ms: u64,
642 #[serde(default = "default_backoff_multiplier")]
644 pub multiplier: f64,
645}
646
647fn default_initial_backoff() -> u64 {
648 100
649}
650
651fn default_max_backoff() -> u64 {
652 10000
653}
654
655fn default_backoff_multiplier() -> f64 {
656 2.0
657}
658
659impl Default for RetryConfig {
660 fn default() -> Self {
661 Self {
662 max_attempts: 3,
663 initial_backoff_ms: 100,
664 max_backoff_ms: 10000,
665 multiplier: 2.0,
666 }
667 }
668}
669
670#[derive(Debug, Clone, Serialize, Deserialize)]
672pub struct IdempotencyConfig {
673 #[serde(default = "default_true")]
675 pub enabled: bool,
676 #[serde(default)]
678 pub storage: IdempotencyStorage,
679 #[serde(default = "default_idempotency_ttl")]
681 pub ttl_secs: u64,
682}
683
684fn default_idempotency_ttl() -> u64 {
685 86400 }
687
688impl Default for IdempotencyConfig {
689 fn default() -> Self {
690 Self {
691 enabled: true,
692 storage: IdempotencyStorage::default(),
693 ttl_secs: 86400,
694 }
695 }
696}
697
698#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
700#[serde(rename_all = "lowercase")]
701pub enum IdempotencyStorage {
702 #[default]
704 Memory,
705 Redis,
707 Postgres,
709}
710
711#[derive(Debug, Clone, Serialize, Deserialize)]
713pub struct ProducerConfig {
714 pub service_name: String,
716 pub display_name: String,
718 #[serde(default)]
720 pub broker: MessageBroker,
721 pub topics: Vec<TopicConfig>,
723 #[serde(default)]
725 pub outbox: OutboxConfig,
726 #[serde(default)]
728 pub server: ServerConfig,
729}
730
731impl Default for ProducerConfig {
732 fn default() -> Self {
733 Self {
734 service_name: "producer".to_string(),
735 display_name: "Event Producer".to_string(),
736 broker: MessageBroker::default(),
737 topics: vec![TopicConfig::default()],
738 outbox: OutboxConfig::default(),
739 server: ServerConfig::default(),
740 }
741 }
742}
743
744#[derive(Debug, Clone, Serialize, Deserialize)]
746pub struct OutboxConfig {
747 #[serde(default = "default_true")]
749 pub enabled: bool,
750 #[serde(default = "default_outbox_table")]
752 pub table_name: String,
753 #[serde(default = "default_polling_interval")]
755 pub polling_interval_ms: u64,
756 #[serde(default = "default_batch_size")]
758 pub batch_size: u32,
759 #[serde(default = "default_max_retries")]
761 pub max_retries: u32,
762}
763
764fn default_outbox_table() -> String {
765 "outbox".to_string()
766}
767
768fn default_polling_interval() -> u64 {
769 1000
770}
771
772fn default_batch_size() -> u32 {
773 100
774}
775
776impl Default for OutboxConfig {
777 fn default() -> Self {
778 Self {
779 enabled: true,
780 table_name: "outbox".to_string(),
781 polling_interval_ms: 1000,
782 batch_size: 100,
783 max_retries: 3,
784 }
785 }
786}
787
788#[derive(Debug, Clone, Serialize, Deserialize)]
790pub struct BffConfig {
791 pub service_name: String,
793 pub display_name: String,
795 pub frontend_type: FrontendType,
797 pub backends: Vec<BackendServiceConfig>,
799 pub graphql_enabled: bool,
801 pub rest_enabled: bool,
803 pub cache: CacheConfig,
805 pub server: ServerConfig,
807}
808
809#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
811pub enum FrontendType {
812 #[default]
814 Web,
815 Mobile,
817 Desktop,
819 Cli,
821}
822
823#[derive(Debug, Clone, Serialize, Deserialize)]
825pub struct BackendServiceConfig {
826 pub name: String,
828 pub base_url: String,
830 pub timeout_ms: u64,
832 pub circuit_breaker: bool,
834}
835
836impl Default for BffConfig {
837 fn default() -> Self {
838 Self {
839 service_name: "bff".to_string(),
840 display_name: "Backend for Frontend".to_string(),
841 frontend_type: FrontendType::Web,
842 backends: vec![BackendServiceConfig {
843 name: "api".to_string(),
844 base_url: "http://localhost:8080".to_string(),
845 timeout_ms: 5000,
846 circuit_breaker: true,
847 }],
848 graphql_enabled: true,
849 rest_enabled: true,
850 cache: CacheConfig::default(),
851 server: ServerConfig::default(),
852 }
853 }
854}
855
856#[derive(Debug, Clone, Serialize, Deserialize)]
858pub struct ScheduledConfig {
859 pub service_name: String,
861 pub display_name: String,
863 pub jobs: Vec<JobConfig>,
865 pub server: ServerConfig,
867}
868
869#[derive(Debug, Clone, Serialize, Deserialize)]
871pub struct JobConfig {
872 pub name: String,
874 pub cron: String,
876 pub description: String,
878 pub enabled: bool,
880 pub timeout_secs: u64,
882}
883
884impl Default for ScheduledConfig {
885 fn default() -> Self {
886 Self {
887 service_name: "scheduler".to_string(),
888 display_name: "Scheduled Jobs".to_string(),
889 jobs: vec![JobConfig {
890 name: "cleanup".to_string(),
891 cron: "0 0 * * *".to_string(),
892 description: "Daily cleanup job".to_string(),
893 enabled: true,
894 timeout_secs: 300,
895 }],
896 server: ServerConfig::default(),
897 }
898 }
899}
900
901#[derive(Debug, Clone, Serialize, Deserialize)]
903pub struct WebSocketGatewayConfig {
904 pub service_name: String,
906 pub display_name: String,
908 pub channels: Vec<ChannelConfig>,
910 pub max_connections_per_client: u32,
912 pub heartbeat_interval_secs: u64,
914 pub connection_timeout_secs: u64,
916 pub server: ServerConfig,
918}
919
920#[derive(Debug, Clone, Serialize, Deserialize)]
922pub struct ChannelConfig {
923 pub name: String,
925 pub description: String,
927 pub authenticated: bool,
929}
930
931impl Default for WebSocketGatewayConfig {
932 fn default() -> Self {
933 Self {
934 service_name: "ws_gateway".to_string(),
935 display_name: "WebSocket Gateway".to_string(),
936 channels: vec![ChannelConfig {
937 name: "events".to_string(),
938 description: "Real-time event stream".to_string(),
939 authenticated: true,
940 }],
941 max_connections_per_client: 5,
942 heartbeat_interval_secs: 30,
943 connection_timeout_secs: 60,
944 server: ServerConfig::default(),
945 }
946 }
947}
948
949#[derive(Debug, Clone, Serialize, Deserialize)]
951pub struct SagaOrchestratorConfig {
952 pub service_name: String,
954 pub display_name: String,
956 pub sagas: Vec<SagaDefinitionConfig>,
958 pub retry: RetryConfig,
960 pub server: ServerConfig,
962}
963
964#[derive(Debug, Clone, Serialize, Deserialize)]
966pub struct SagaDefinitionConfig {
967 pub name: String,
969 pub description: String,
971 pub steps: Vec<String>,
973}
974
975impl Default for SagaOrchestratorConfig {
976 fn default() -> Self {
977 Self {
978 service_name: "saga_orchestrator".to_string(),
979 display_name: "Saga Orchestrator".to_string(),
980 sagas: vec![SagaDefinitionConfig {
981 name: "order_saga".to_string(),
982 description: "Order processing saga".to_string(),
983 steps: vec![
984 "validate_order".to_string(),
985 "reserve_inventory".to_string(),
986 "process_payment".to_string(),
987 "send_notification".to_string(),
988 ],
989 }],
990 retry: RetryConfig::default(),
991 server: ServerConfig::default(),
992 }
993 }
994}
995
996#[derive(Debug, Clone, Serialize, Deserialize)]
998pub struct AntiCorruptionLayerConfig {
999 pub service_name: String,
1001 pub display_name: String,
1003 pub legacy_system: LegacySystemConfig,
1005 pub transformations: Vec<TransformationConfig>,
1007 pub server: ServerConfig,
1009}
1010
1011#[derive(Debug, Clone, Serialize, Deserialize)]
1013pub struct LegacySystemConfig {
1014 pub name: String,
1016 pub connection_type: LegacyConnectionType,
1018 pub connection_string: String,
1020 pub timeout_ms: u64,
1022}
1023
1024#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1026pub enum LegacyConnectionType {
1027 #[default]
1029 Rest,
1030 Soap,
1032 Database,
1034 File,
1036 Mq,
1038}
1039
1040#[derive(Debug, Clone, Serialize, Deserialize)]
1042pub struct TransformationConfig {
1043 pub source: String,
1045 pub target: String,
1047 pub description: String,
1049}
1050
1051impl Default for AntiCorruptionLayerConfig {
1052 fn default() -> Self {
1053 Self {
1054 service_name: "acl".to_string(),
1055 display_name: "Anti-Corruption Layer".to_string(),
1056 legacy_system: LegacySystemConfig {
1057 name: "legacy_api".to_string(),
1058 connection_type: LegacyConnectionType::Rest,
1059 connection_string: "http://legacy-system:8080".to_string(),
1060 timeout_ms: 10000,
1061 },
1062 transformations: vec![TransformationConfig {
1063 source: "LegacyEntity".to_string(),
1064 target: "ModernEntity".to_string(),
1065 description: "Transform legacy entity to modern format".to_string(),
1066 }],
1067 server: ServerConfig::default(),
1068 }
1069 }
1070}
1071
1072#[cfg(test)]
1073mod tests {
1074 use super::*;
1075
1076 #[test]
1077 fn test_archetype_from_str() {
1078 assert_eq!("basic".parse::<Archetype>().unwrap(), Archetype::Basic);
1079 assert_eq!("gateway".parse::<Archetype>().unwrap(), Archetype::Gateway);
1080 assert_eq!(
1081 "event-sourced".parse::<Archetype>().unwrap(),
1082 Archetype::EventSourced
1083 );
1084 assert_eq!(
1085 "cqrs".parse::<Archetype>().unwrap(),
1086 Archetype::EventSourced
1087 );
1088 assert_eq!(
1089 "consumer".parse::<Archetype>().unwrap(),
1090 Archetype::Consumer
1091 );
1092 assert_eq!(
1093 "producer".parse::<Archetype>().unwrap(),
1094 Archetype::Producer
1095 );
1096 }
1097
1098 #[test]
1099 fn test_project_config_default() {
1100 let config = ProjectConfig::new("test-project");
1101 assert_eq!(config.name, "test-project");
1102 assert_eq!(config.archetype, Archetype::Basic);
1103 assert!(config.tracing);
1104 assert!(config.metrics);
1105 }
1106
1107 #[test]
1108 fn test_gateway_config_default() {
1109 let config = GatewayConfig::default();
1110 assert_eq!(config.rate_limit.public_rps, 10);
1111 assert_eq!(config.cache.public_ttl_secs, 300);
1112 }
1113
1114 #[test]
1115 fn test_consumer_config_default() {
1116 let config = ConsumerConfig::default();
1117 assert_eq!(config.broker, MessageBroker::Kafka);
1118 assert!(config.dlq.enabled);
1119 assert!(config.idempotency.enabled);
1120 assert_eq!(config.retry.max_attempts, 3);
1121 }
1122
1123 #[test]
1124 fn test_producer_config_default() {
1125 let config = ProducerConfig::default();
1126 assert_eq!(config.broker, MessageBroker::Kafka);
1127 assert!(config.outbox.enabled);
1128 assert_eq!(config.outbox.batch_size, 100);
1129 }
1130
1131 #[test]
1132 fn test_with_archetype_consumer() {
1133 let config = ProjectConfig::new("test").with_archetype(Archetype::Consumer);
1134 assert!(config.consumer.is_some());
1135 }
1136
1137 #[test]
1138 fn test_with_archetype_producer() {
1139 let config = ProjectConfig::new("test").with_archetype(Archetype::Producer);
1140 assert!(config.producer.is_some());
1141 }
1142}