1use serde::{Deserialize, Serialize};
7use std::time::Duration;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
11#[serde(rename_all = "snake_case")]
12pub enum Archetype {
13 #[default]
15 Basic,
16 Gateway,
18 EventSourced,
20 Consumer,
22 Producer,
24 Bff,
26 Scheduled,
28 WebSocketGateway,
30 SagaOrchestrator,
32 AntiCorruptionLayer,
34}
35
36impl std::fmt::Display for Archetype {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 match self {
39 Self::Basic => write!(f, "basic"),
40 Self::Gateway => write!(f, "gateway"),
41 Self::EventSourced => write!(f, "event-sourced"),
42 Self::Consumer => write!(f, "consumer"),
43 Self::Producer => write!(f, "producer"),
44 Self::Bff => write!(f, "bff"),
45 Self::Scheduled => write!(f, "scheduled"),
46 Self::WebSocketGateway => write!(f, "websocket-gateway"),
47 Self::SagaOrchestrator => write!(f, "saga-orchestrator"),
48 Self::AntiCorruptionLayer => write!(f, "anti-corruption-layer"),
49 }
50 }
51}
52
53impl std::str::FromStr for Archetype {
54 type Err = String;
55
56 fn from_str(s: &str) -> Result<Self, Self::Err> {
57 match s.to_lowercase().as_str() {
58 "basic" => Ok(Self::Basic),
59 "gateway" => Ok(Self::Gateway),
60 "event-sourced" | "eventsourced" | "cqrs" => Ok(Self::EventSourced),
61 "consumer" => Ok(Self::Consumer),
62 "producer" => Ok(Self::Producer),
63 "bff" | "backend-for-frontend" => Ok(Self::Bff),
64 "scheduled" | "cron" | "jobs" => Ok(Self::Scheduled),
65 "websocket-gateway" | "websocket" | "ws" => Ok(Self::WebSocketGateway),
66 "saga-orchestrator" | "saga" => Ok(Self::SagaOrchestrator),
67 "anti-corruption-layer" | "acl" | "legacy-adapter" => Ok(Self::AntiCorruptionLayer),
68 _ => Err(format!("Unknown archetype: {}", s)),
69 }
70 }
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
75#[serde(rename_all = "lowercase")]
76pub enum Protocol {
77 Rest,
79 Graphql,
81 Grpc,
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
87#[serde(rename_all = "snake_case")]
88pub enum AuthMethod {
89 #[default]
91 None,
92 ApiKey,
94 HmacSha256,
96 HmacSha512Base64,
98 OAuth2,
100 Jwt,
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
106#[serde(rename_all = "lowercase")]
107pub enum CacheBackend {
108 #[default]
110 Memory,
111 Redis,
113 None,
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
119#[serde(rename_all = "lowercase")]
120pub enum MessageBroker {
121 #[default]
123 Kafka,
124 RabbitMq,
126 Redis,
128 Sqs,
130 PubSub,
132}
133
134impl std::fmt::Display for MessageBroker {
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 match self {
137 Self::Kafka => write!(f, "kafka"),
138 Self::RabbitMq => write!(f, "rabbitmq"),
139 Self::Redis => write!(f, "redis"),
140 Self::Sqs => write!(f, "sqs"),
141 Self::PubSub => write!(f, "pubsub"),
142 }
143 }
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct ProjectConfig {
149 pub name: String,
151 #[serde(default)]
153 pub archetype: Archetype,
154 #[serde(default)]
156 pub protocols: Vec<Protocol>,
157 #[serde(default = "default_true")]
159 pub tracing: bool,
160 #[serde(default = "default_true")]
162 pub metrics: bool,
163 #[serde(default)]
165 pub gateway: Option<GatewayConfig>,
166 #[serde(default)]
168 pub consumer: Option<ConsumerConfig>,
169 #[serde(default)]
171 pub producer: Option<ProducerConfig>,
172 #[serde(default)]
174 pub bff: Option<BffConfig>,
175 #[serde(default)]
177 pub scheduled: Option<ScheduledConfig>,
178 #[serde(default)]
180 pub websocket_gateway: Option<WebSocketGatewayConfig>,
181 #[serde(default)]
183 pub saga_orchestrator: Option<SagaOrchestratorConfig>,
184 #[serde(default)]
186 pub acl: Option<AntiCorruptionLayerConfig>,
187}
188
189fn default_true() -> bool {
190 true
191}
192
193impl Default for ProjectConfig {
194 fn default() -> Self {
195 Self {
196 name: String::new(),
197 archetype: Archetype::default(),
198 protocols: vec![Protocol::Grpc],
199 tracing: true,
200 metrics: true,
201 gateway: None,
202 consumer: None,
203 producer: None,
204 bff: None,
205 scheduled: None,
206 websocket_gateway: None,
207 saga_orchestrator: None,
208 acl: None,
209 }
210 }
211}
212
213impl ProjectConfig {
214 pub fn new(name: impl Into<String>) -> Self {
216 Self {
217 name: name.into(),
218 ..Default::default()
219 }
220 }
221
222 pub fn with_archetype(mut self, archetype: Archetype) -> Self {
224 self.archetype = archetype;
225 match archetype {
226 Archetype::Gateway if self.gateway.is_none() => {
227 self.gateway = Some(GatewayConfig::default());
228 }
229 Archetype::Consumer if self.consumer.is_none() => {
230 self.consumer = Some(ConsumerConfig::default());
231 }
232 Archetype::Producer if self.producer.is_none() => {
233 self.producer = Some(ProducerConfig::default());
234 }
235 Archetype::Bff if self.bff.is_none() => {
236 self.bff = Some(BffConfig::default());
237 }
238 Archetype::Scheduled if self.scheduled.is_none() => {
239 self.scheduled = Some(ScheduledConfig::default());
240 }
241 Archetype::WebSocketGateway if self.websocket_gateway.is_none() => {
242 self.websocket_gateway = Some(WebSocketGatewayConfig::default());
243 }
244 Archetype::SagaOrchestrator if self.saga_orchestrator.is_none() => {
245 self.saga_orchestrator = Some(SagaOrchestratorConfig::default());
246 }
247 Archetype::AntiCorruptionLayer if self.acl.is_none() => {
248 self.acl = Some(AntiCorruptionLayerConfig::default());
249 }
250 _ => {}
251 }
252 self
253 }
254
255 pub fn with_protocols(mut self, protocols: Vec<Protocol>) -> Self {
257 self.protocols = protocols;
258 self
259 }
260
261 pub fn with_gateway(mut self, gateway: GatewayConfig) -> Self {
263 self.gateway = Some(gateway);
264 self
265 }
266
267 pub fn with_consumer(mut self, consumer: ConsumerConfig) -> Self {
269 self.consumer = Some(consumer);
270 self
271 }
272
273 pub fn with_producer(mut self, producer: ProducerConfig) -> Self {
275 self.producer = Some(producer);
276 self
277 }
278
279 pub fn with_bff(mut self, bff: BffConfig) -> Self {
281 self.bff = Some(bff);
282 self
283 }
284
285 pub fn with_scheduled(mut self, scheduled: ScheduledConfig) -> Self {
287 self.scheduled = Some(scheduled);
288 self
289 }
290
291 pub fn with_websocket_gateway(mut self, ws: WebSocketGatewayConfig) -> Self {
293 self.websocket_gateway = Some(ws);
294 self
295 }
296
297 pub fn with_saga_orchestrator(mut self, saga: SagaOrchestratorConfig) -> Self {
299 self.saga_orchestrator = Some(saga);
300 self
301 }
302
303 pub fn with_acl(mut self, acl: AntiCorruptionLayerConfig) -> Self {
305 self.acl = Some(acl);
306 self
307 }
308}
309
310#[derive(Debug, Clone, Serialize, Deserialize)]
312pub struct GatewayConfig {
313 pub service_name: String,
315 pub display_name: String,
317 pub api_base_url: String,
319 #[serde(default)]
321 pub auth_method: AuthMethod,
322 #[serde(default)]
324 pub rate_limit: RateLimitConfig,
325 #[serde(default)]
327 pub cache: CacheConfig,
328 #[serde(default)]
330 pub server: ServerConfig,
331 #[serde(default)]
333 pub entities: Vec<EntityConfig>,
334 #[serde(default)]
336 pub endpoints: Vec<EndpointConfig>,
337}
338
339impl Default for GatewayConfig {
340 fn default() -> Self {
341 Self {
342 service_name: "exchange".to_string(),
343 display_name: "Exchange Gateway".to_string(),
344 api_base_url: "https://api.example.com".to_string(),
345 auth_method: AuthMethod::default(),
346 rate_limit: RateLimitConfig::default(),
347 cache: CacheConfig::default(),
348 server: ServerConfig::default(),
349 entities: vec![],
350 endpoints: vec![],
351 }
352 }
353}
354
355#[derive(Debug, Clone, Serialize, Deserialize)]
357pub struct RateLimitConfig {
358 pub public_rps: u32,
360 pub private_rps: u32,
362 pub burst: u32,
364}
365
366impl Default for RateLimitConfig {
367 fn default() -> Self {
368 Self {
369 public_rps: 10,
370 private_rps: 2,
371 burst: 5,
372 }
373 }
374}
375
376#[derive(Debug, Clone, Serialize, Deserialize)]
378pub struct CacheConfig {
379 #[serde(default = "default_true")]
381 pub enabled: bool,
382 #[serde(default)]
384 pub backend: CacheBackend,
385 #[serde(default = "default_public_ttl")]
387 pub public_ttl_secs: u64,
388 #[serde(default = "default_private_ttl")]
390 pub private_ttl_secs: u64,
391}
392
393fn default_public_ttl() -> u64 {
394 300
395}
396
397fn default_private_ttl() -> u64 {
398 60
399}
400
401impl Default for CacheConfig {
402 fn default() -> Self {
403 Self {
404 enabled: true,
405 backend: CacheBackend::Memory,
406 public_ttl_secs: 300,
407 private_ttl_secs: 60,
408 }
409 }
410}
411
412impl CacheConfig {
413 pub fn public_ttl(&self) -> Duration {
415 Duration::from_secs(self.public_ttl_secs)
416 }
417
418 pub fn private_ttl(&self) -> Duration {
420 Duration::from_secs(self.private_ttl_secs)
421 }
422}
423
424#[derive(Debug, Clone, Serialize, Deserialize)]
426pub struct ServerConfig {
427 #[serde(default = "default_http_port")]
429 pub http_port: u16,
430 #[serde(default = "default_grpc_port")]
432 pub grpc_port: u16,
433 #[serde(default = "default_health_port")]
435 pub health_port: u16,
436 #[serde(default = "default_metrics_port")]
438 pub metrics_port: u16,
439}
440
441fn default_http_port() -> u16 {
442 8080
443}
444
445fn default_grpc_port() -> u16 {
446 50051
447}
448
449fn default_health_port() -> u16 {
450 8081
451}
452
453fn default_metrics_port() -> u16 {
454 9090
455}
456
457impl Default for ServerConfig {
458 fn default() -> Self {
459 Self {
460 http_port: 8080,
461 grpc_port: 50051,
462 health_port: 8081,
463 metrics_port: 9090,
464 }
465 }
466}
467
468#[derive(Debug, Clone, Serialize, Deserialize)]
470pub struct EntityConfig {
471 pub name: String,
473 pub fields: Vec<FieldConfig>,
475}
476
477#[derive(Debug, Clone, Serialize, Deserialize)]
479pub struct FieldConfig {
480 pub name: String,
482 pub field_type: String,
484 #[serde(default)]
486 pub optional: bool,
487}
488
489#[derive(Debug, Clone, Serialize, Deserialize)]
491pub struct EndpointConfig {
492 pub name: String,
494 pub method: String,
496 pub path: String,
498 #[serde(default)]
500 pub private: bool,
501 #[serde(default)]
503 pub params: Vec<ParamConfig>,
504 pub response_type: String,
506 #[serde(default)]
508 pub cacheable: bool,
509}
510
511#[derive(Debug, Clone, Serialize, Deserialize)]
513pub struct ParamConfig {
514 pub name: String,
516 pub param_type: String,
518 #[serde(default = "default_true")]
520 pub required: bool,
521}
522
523#[derive(Debug, Clone, Serialize, Deserialize)]
525pub struct ConsumerConfig {
526 pub service_name: String,
528 pub display_name: String,
530 #[serde(default)]
532 pub broker: MessageBroker,
533 pub topics: Vec<TopicConfig>,
535 pub group_id: String,
537 #[serde(default)]
539 pub dlq: DlqConfig,
540 #[serde(default)]
542 pub retry: RetryConfig,
543 #[serde(default)]
545 pub idempotency: IdempotencyConfig,
546 #[serde(default)]
548 pub server: ServerConfig,
549}
550
551impl Default for ConsumerConfig {
552 fn default() -> Self {
553 Self {
554 service_name: "consumer".to_string(),
555 display_name: "Event Consumer".to_string(),
556 broker: MessageBroker::default(),
557 topics: vec![TopicConfig::default()],
558 group_id: "consumer-group".to_string(),
559 dlq: DlqConfig::default(),
560 retry: RetryConfig::default(),
561 idempotency: IdempotencyConfig::default(),
562 server: ServerConfig::default(),
563 }
564 }
565}
566
567#[derive(Debug, Clone, Serialize, Deserialize)]
569pub struct TopicConfig {
570 pub name: String,
572 pub event_type: String,
574 #[serde(default = "default_partitions")]
576 pub partitions: u32,
577 #[serde(default)]
579 pub auto_commit: bool,
580}
581
582fn default_partitions() -> u32 {
583 1
584}
585
586impl Default for TopicConfig {
587 fn default() -> Self {
588 Self {
589 name: "events".to_string(),
590 event_type: "Event".to_string(),
591 partitions: 1,
592 auto_commit: false,
593 }
594 }
595}
596
597#[derive(Debug, Clone, Serialize, Deserialize)]
599pub struct DlqConfig {
600 #[serde(default = "default_true")]
602 pub enabled: bool,
603 #[serde(default = "default_dlq_suffix")]
605 pub suffix: String,
606 #[serde(default = "default_max_retries")]
608 pub max_retries: u32,
609}
610
611fn default_dlq_suffix() -> String {
612 ".dlq".to_string()
613}
614
615fn default_max_retries() -> u32 {
616 3
617}
618
619impl Default for DlqConfig {
620 fn default() -> Self {
621 Self {
622 enabled: true,
623 suffix: ".dlq".to_string(),
624 max_retries: 3,
625 }
626 }
627}
628
629#[derive(Debug, Clone, Serialize, Deserialize)]
631pub struct RetryConfig {
632 #[serde(default = "default_max_retries")]
634 pub max_attempts: u32,
635 #[serde(default = "default_initial_backoff")]
637 pub initial_backoff_ms: u64,
638 #[serde(default = "default_max_backoff")]
640 pub max_backoff_ms: u64,
641 #[serde(default = "default_backoff_multiplier")]
643 pub multiplier: f64,
644}
645
646fn default_initial_backoff() -> u64 {
647 100
648}
649
650fn default_max_backoff() -> u64 {
651 10000
652}
653
654fn default_backoff_multiplier() -> f64 {
655 2.0
656}
657
658impl Default for RetryConfig {
659 fn default() -> Self {
660 Self {
661 max_attempts: 3,
662 initial_backoff_ms: 100,
663 max_backoff_ms: 10000,
664 multiplier: 2.0,
665 }
666 }
667}
668
669#[derive(Debug, Clone, Serialize, Deserialize)]
671pub struct IdempotencyConfig {
672 #[serde(default = "default_true")]
674 pub enabled: bool,
675 #[serde(default)]
677 pub storage: IdempotencyStorage,
678 #[serde(default = "default_idempotency_ttl")]
680 pub ttl_secs: u64,
681}
682
683fn default_idempotency_ttl() -> u64 {
684 86400 }
686
687impl Default for IdempotencyConfig {
688 fn default() -> Self {
689 Self {
690 enabled: true,
691 storage: IdempotencyStorage::default(),
692 ttl_secs: 86400,
693 }
694 }
695}
696
697#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
699#[serde(rename_all = "lowercase")]
700pub enum IdempotencyStorage {
701 #[default]
703 Memory,
704 Redis,
706 Postgres,
708}
709
710#[derive(Debug, Clone, Serialize, Deserialize)]
712pub struct ProducerConfig {
713 pub service_name: String,
715 pub display_name: String,
717 #[serde(default)]
719 pub broker: MessageBroker,
720 pub topics: Vec<TopicConfig>,
722 #[serde(default)]
724 pub outbox: OutboxConfig,
725 #[serde(default)]
727 pub server: ServerConfig,
728}
729
730impl Default for ProducerConfig {
731 fn default() -> Self {
732 Self {
733 service_name: "producer".to_string(),
734 display_name: "Event Producer".to_string(),
735 broker: MessageBroker::default(),
736 topics: vec![TopicConfig::default()],
737 outbox: OutboxConfig::default(),
738 server: ServerConfig::default(),
739 }
740 }
741}
742
743#[derive(Debug, Clone, Serialize, Deserialize)]
745pub struct OutboxConfig {
746 #[serde(default = "default_true")]
748 pub enabled: bool,
749 #[serde(default = "default_outbox_table")]
751 pub table_name: String,
752 #[serde(default = "default_polling_interval")]
754 pub polling_interval_ms: u64,
755 #[serde(default = "default_batch_size")]
757 pub batch_size: u32,
758 #[serde(default = "default_max_retries")]
760 pub max_retries: u32,
761}
762
763fn default_outbox_table() -> String {
764 "outbox".to_string()
765}
766
767fn default_polling_interval() -> u64 {
768 1000
769}
770
771fn default_batch_size() -> u32 {
772 100
773}
774
775impl Default for OutboxConfig {
776 fn default() -> Self {
777 Self {
778 enabled: true,
779 table_name: "outbox".to_string(),
780 polling_interval_ms: 1000,
781 batch_size: 100,
782 max_retries: 3,
783 }
784 }
785}
786
787#[derive(Debug, Clone, Serialize, Deserialize)]
789pub struct BffConfig {
790 pub service_name: String,
792 pub display_name: String,
794 pub frontend_type: FrontendType,
796 pub backends: Vec<BackendServiceConfig>,
798 pub graphql_enabled: bool,
800 pub rest_enabled: bool,
802 pub cache: CacheConfig,
804 pub server: ServerConfig,
806}
807
808#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
810pub enum FrontendType {
811 #[default]
813 Web,
814 Mobile,
816 Desktop,
818 Cli,
820}
821
822#[derive(Debug, Clone, Serialize, Deserialize)]
824pub struct BackendServiceConfig {
825 pub name: String,
827 pub base_url: String,
829 pub timeout_ms: u64,
831 pub circuit_breaker: bool,
833}
834
835impl Default for BffConfig {
836 fn default() -> Self {
837 Self {
838 service_name: "bff".to_string(),
839 display_name: "Backend for Frontend".to_string(),
840 frontend_type: FrontendType::Web,
841 backends: vec![BackendServiceConfig {
842 name: "api".to_string(),
843 base_url: "http://localhost:8080".to_string(),
844 timeout_ms: 5000,
845 circuit_breaker: true,
846 }],
847 graphql_enabled: true,
848 rest_enabled: true,
849 cache: CacheConfig::default(),
850 server: ServerConfig::default(),
851 }
852 }
853}
854
855#[derive(Debug, Clone, Serialize, Deserialize)]
857pub struct ScheduledConfig {
858 pub service_name: String,
860 pub display_name: String,
862 pub jobs: Vec<JobConfig>,
864 pub server: ServerConfig,
866}
867
868#[derive(Debug, Clone, Serialize, Deserialize)]
870pub struct JobConfig {
871 pub name: String,
873 pub cron: String,
875 pub description: String,
877 pub enabled: bool,
879 pub timeout_secs: u64,
881}
882
883impl Default for ScheduledConfig {
884 fn default() -> Self {
885 Self {
886 service_name: "scheduler".to_string(),
887 display_name: "Scheduled Jobs".to_string(),
888 jobs: vec![JobConfig {
889 name: "cleanup".to_string(),
890 cron: "0 0 * * *".to_string(),
891 description: "Daily cleanup job".to_string(),
892 enabled: true,
893 timeout_secs: 300,
894 }],
895 server: ServerConfig::default(),
896 }
897 }
898}
899
900#[derive(Debug, Clone, Serialize, Deserialize)]
902pub struct WebSocketGatewayConfig {
903 pub service_name: String,
905 pub display_name: String,
907 pub channels: Vec<ChannelConfig>,
909 pub max_connections_per_client: u32,
911 pub heartbeat_interval_secs: u64,
913 pub connection_timeout_secs: u64,
915 pub server: ServerConfig,
917}
918
919#[derive(Debug, Clone, Serialize, Deserialize)]
921pub struct ChannelConfig {
922 pub name: String,
924 pub description: String,
926 pub authenticated: bool,
928}
929
930impl Default for WebSocketGatewayConfig {
931 fn default() -> Self {
932 Self {
933 service_name: "ws_gateway".to_string(),
934 display_name: "WebSocket Gateway".to_string(),
935 channels: vec![ChannelConfig {
936 name: "events".to_string(),
937 description: "Real-time event stream".to_string(),
938 authenticated: true,
939 }],
940 max_connections_per_client: 5,
941 heartbeat_interval_secs: 30,
942 connection_timeout_secs: 60,
943 server: ServerConfig::default(),
944 }
945 }
946}
947
948#[derive(Debug, Clone, Serialize, Deserialize)]
950pub struct SagaOrchestratorConfig {
951 pub service_name: String,
953 pub display_name: String,
955 pub sagas: Vec<SagaDefinitionConfig>,
957 pub retry: RetryConfig,
959 pub server: ServerConfig,
961}
962
963#[derive(Debug, Clone, Serialize, Deserialize)]
965pub struct SagaDefinitionConfig {
966 pub name: String,
968 pub description: String,
970 pub steps: Vec<String>,
972}
973
974impl Default for SagaOrchestratorConfig {
975 fn default() -> Self {
976 Self {
977 service_name: "saga_orchestrator".to_string(),
978 display_name: "Saga Orchestrator".to_string(),
979 sagas: vec![SagaDefinitionConfig {
980 name: "order_saga".to_string(),
981 description: "Order processing saga".to_string(),
982 steps: vec![
983 "validate_order".to_string(),
984 "reserve_inventory".to_string(),
985 "process_payment".to_string(),
986 "send_notification".to_string(),
987 ],
988 }],
989 retry: RetryConfig::default(),
990 server: ServerConfig::default(),
991 }
992 }
993}
994
995#[derive(Debug, Clone, Serialize, Deserialize)]
997pub struct AntiCorruptionLayerConfig {
998 pub service_name: String,
1000 pub display_name: String,
1002 pub legacy_system: LegacySystemConfig,
1004 pub transformations: Vec<TransformationConfig>,
1006 pub server: ServerConfig,
1008}
1009
1010#[derive(Debug, Clone, Serialize, Deserialize)]
1012pub struct LegacySystemConfig {
1013 pub name: String,
1015 pub connection_type: LegacyConnectionType,
1017 pub connection_string: String,
1019 pub timeout_ms: u64,
1021}
1022
1023#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1025pub enum LegacyConnectionType {
1026 #[default]
1028 Rest,
1029 Soap,
1031 Database,
1033 File,
1035 Mq,
1037}
1038
1039#[derive(Debug, Clone, Serialize, Deserialize)]
1041pub struct TransformationConfig {
1042 pub source: String,
1044 pub target: String,
1046 pub description: String,
1048}
1049
1050impl Default for AntiCorruptionLayerConfig {
1051 fn default() -> Self {
1052 Self {
1053 service_name: "acl".to_string(),
1054 display_name: "Anti-Corruption Layer".to_string(),
1055 legacy_system: LegacySystemConfig {
1056 name: "legacy_api".to_string(),
1057 connection_type: LegacyConnectionType::Rest,
1058 connection_string: "http://legacy-system:8080".to_string(),
1059 timeout_ms: 10000,
1060 },
1061 transformations: vec![TransformationConfig {
1062 source: "LegacyEntity".to_string(),
1063 target: "ModernEntity".to_string(),
1064 description: "Transform legacy entity to modern format".to_string(),
1065 }],
1066 server: ServerConfig::default(),
1067 }
1068 }
1069}
1070
1071#[cfg(test)]
1072mod tests {
1073 use super::*;
1074
1075 #[test]
1076 fn test_archetype_from_str() {
1077 assert_eq!("basic".parse::<Archetype>().unwrap(), Archetype::Basic);
1078 assert_eq!("gateway".parse::<Archetype>().unwrap(), Archetype::Gateway);
1079 assert_eq!(
1080 "event-sourced".parse::<Archetype>().unwrap(),
1081 Archetype::EventSourced
1082 );
1083 assert_eq!("cqrs".parse::<Archetype>().unwrap(), Archetype::EventSourced);
1084 assert_eq!("consumer".parse::<Archetype>().unwrap(), Archetype::Consumer);
1085 assert_eq!("producer".parse::<Archetype>().unwrap(), Archetype::Producer);
1086 }
1087
1088 #[test]
1089 fn test_project_config_default() {
1090 let config = ProjectConfig::new("test-project");
1091 assert_eq!(config.name, "test-project");
1092 assert_eq!(config.archetype, Archetype::Basic);
1093 assert!(config.tracing);
1094 assert!(config.metrics);
1095 }
1096
1097 #[test]
1098 fn test_gateway_config_default() {
1099 let config = GatewayConfig::default();
1100 assert_eq!(config.rate_limit.public_rps, 10);
1101 assert_eq!(config.cache.public_ttl_secs, 300);
1102 }
1103
1104 #[test]
1105 fn test_consumer_config_default() {
1106 let config = ConsumerConfig::default();
1107 assert_eq!(config.broker, MessageBroker::Kafka);
1108 assert!(config.dlq.enabled);
1109 assert!(config.idempotency.enabled);
1110 assert_eq!(config.retry.max_attempts, 3);
1111 }
1112
1113 #[test]
1114 fn test_producer_config_default() {
1115 let config = ProducerConfig::default();
1116 assert_eq!(config.broker, MessageBroker::Kafka);
1117 assert!(config.outbox.enabled);
1118 assert_eq!(config.outbox.batch_size, 100);
1119 }
1120
1121 #[test]
1122 fn test_with_archetype_consumer() {
1123 let config = ProjectConfig::new("test").with_archetype(Archetype::Consumer);
1124 assert!(config.consumer.is_some());
1125 }
1126
1127 #[test]
1128 fn test_with_archetype_producer() {
1129 let config = ProjectConfig::new("test").with_archetype(Archetype::Producer);
1130 assert!(config.producer.is_some());
1131 }
1132}