Skip to content

basic app

mf2024 edited this page Feb 17, 2026 · 2 revisions

Basic Application Example

Version: 0.1.7

Last modified date: 2026-02-17

This example shows how to build a simple DMSC application, including application configuration, running, and basic functionality usage.

Example Overview

This example will create a basic DMSC application that implements the following features:

  • Load configuration files
  • Enable logging
  • Enable observability
  • Output a startup log

Prerequisites

  • Rust 1.65+
  • Cargo 1.65+
  • Basic Rust programming knowledge

Example Code

1. Create a Project

cargo new dms-basic-example
cd dms-basic-example

2. Add Dependencies

Add the following dependencies to your Cargo.toml file:

[dependencies]
dmsc = { path = "../dmsc" }
tokio = { version = "1.0", features = ["full"] }

3. Create Configuration File

Create a config.yaml file in the project root:

service:
  name: "dms-basic-example"
  version: "1.0.0"

logging:
  level: "info"
  format: "json"
  file_enabled: false
  console_enabled: true

observability:
  metrics_enabled: true
  tracing_enabled: true
  prometheus_port: 9090

4. Write Main Code

Replace the content of src/main.rs with the following:

use dmsc::prelude::*;

#[tokio::main]
async fn main() -> DMSCResult<()> {
    // Build service runtime
    let app = DMSCAppBuilder::new()
        .with_config("config.yaml")?
        .with_logging(DMSCLogConfig::default())
        .with_observability(DMSCObservabilityConfig::default())
        .build()?;
    
    // Run business logic
    app.run(|ctx: &DMSCServiceContext| async move {
        // Get service name and version
        let service_name = ctx.config().config().get_str("service.name").unwrap_or("unknown");
        let service_version = ctx.config().config().get_str("service.version").unwrap_or("unknown");
        
        // Output startup log
        ctx.logger().info(
            "service", 
            &format!("{} v{} started successfully", service_name, service_version)
        )?;
        
        // Output configuration info
        let log_level = ctx.config().config().get_str("logging.level").unwrap_or("info");
        ctx.logger().info(
            "config", 
            &format!("Logging level: {}", log_level)
        )?;
        
        // Wait 3 seconds to simulate business running
        tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
        
        ctx.logger().info("service", "Service finished successfully")?;
        
        Ok(())
    }).await
}

Code Explanation

1. Import Dependencies

use dmsc::prelude::*;

This line imports the most commonly used types and traits from DMSC, simplifying code writing. The prelude module contains core components needed for building DMSC applications.

2. Main Function

#[tokio::main]
async fn main() -> DMSCResult<()> {
    // Code...
}
  • #[tokio::main]: Converts the main function into an async function and executes it using the Tokio runtime
  • async fn main(): Defines an asynchronous main function
  • -> DMSCResult<()>: Returns DMSC result type for error handling

3. Build Application

let app = DMSCAppBuilder::new()
    .with_config("config.yaml")?
    .with_logging(DMSCLogConfig::default())?
    .with_observability(DMSCObservabilityConfig::default())?
    .build()?;
  • DMSCAppBuilder::new(): Creates a new application builder
  • .with_config("config.yaml")?: Loads the configuration file
  • .with_logging(DMSCLogConfig::default())?: Enables logging with default configuration
  • .with_observability(DMSCObservabilityConfig::default())?: Enables observability with default configuration
  • .build()?: Builds the application runtime

4. Run Application

app.run(|ctx: &DMSCServiceContext| async move {
    // Business logic
}).await
  • app.run(): Starts the application runtime
  • |ctx: &DMSCServiceContext|: Closure parameter that receives the service context
  • async move: Async closure that allows using await inside the closure

5. Business Logic

let service_name = ctx.config().config().get_str("service.name").unwrap_or("unknown");
let service_version = ctx.config().config().get_str("service.version").unwrap_or("unknown");

ctx.logger().info(
    "service", 
    &format!("{} v{} started successfully", service_name, service_version)
)?;
  • ctx.config().config().get_str(): Gets a value from the configuration
  • ctx.logger().info(): Records an info-level log
  • ?: Propagates errors

Running Steps

1. Build the Project

cargo build

2. Run the Project

cargo run

Expected Results

After running the example, you should see output similar to the following:

{
  "timestamp": "2025-12-12T15:30:00Z",
  "level": "info",
  "module": "service",
  "message": "dms-basic-example v1.0.0 started successfully",
  "trace_id": "abc123",
  "span_id": "def456"
}
{
  "timestamp": "2025-12-12T15:30:00Z",
  "level": "info",
  "module": "config",
  "message": "Logging level: info",
  "trace_id": "abc123",
  "span_id": "def456"
}
{
  "timestamp": "2025-12-12T15:30:03Z",
  "level": "info",
  "module": "service",
  "message": "Service finished successfully",
  "trace_id": "abc123",
  "span_id": "def456"
}

Extending Functionality

1. Implementing Cache Support

Note: Cache functionality needs to be separately configured and enabled through the module system. Cache module is accessed via DMSCCacheModule.

// Add cache module
let cache_module = DMSCCacheModule::new(DMSCCacheConfig::default());

let app = DMSCAppBuilder::new()
    .with_config("config.yaml")?
    .with_logging(DMSCLogConfig::default())?
    .with_dms_module(Box::new(cache_module))?
    .build()?;

// Use cache in business logic
app.run(|ctx: &DMSCServiceContext| async move {
    // Get cache module
    let cache = ctx.module::<DMSCCacheModule>().await?;
    let cache_manager = cache.cache_manager();
    
    // Set cache
    cache_manager.set("key", "value", Some(3600)).await?;
    
    // Get cache
    let value: Option<String> = cache_manager.get("key").await?;
    ctx.logger().info("cache", &format!("Cached value: {:?}", value))?;
    
    Ok(())
}).await

2. Implementing Queue Support

Note: Queue functionality needs to be separately configured and enabled through the module system. Queue module is accessed via DMSCQueueModule.

// Add queue module
let queue_module = DMSCQueueModule::new(DMSCQueueConfig::default());

let app = DMSCAppBuilder::new()
    .with_config("config.yaml")?
    .with_logging(DMSCLogConfig::default())?
    .with_dms_module(Box::new(queue_module))?
    .build()?;

// Use queue in business logic
app.run(|ctx: &DMSCServiceContext| async move {
    // Get queue module
    let queue = ctx.module::<DMSCQueueModule>().await?;
    let queue_manager = queue.queue_manager();
    
    // Get or create queue
    let queue = queue_manager.create_queue("task_queue").await?;
    
    // Send message
    queue.publish(&DMSCQueueMessage::new("task-123", json!({
        "task_type": "data_processing",
        "priority": 1,
    }))).await?;
    
    ctx.logger().info("queue", "Task message sent to queue")?;
    
    Ok(())
}).await

3. Implementing File System Operations

// Use file system in business logic
app.run(|ctx: &DMSCServiceContext| async move {
    // Write to file
    ctx.fs().atomic_write_text("data/config.json", r#"{"setting": "value"}"#)?;
    
    // Read from file
    let content = ctx.fs().read_text("data/config.json")?;
    ctx.logger().info("fs", &format!("File content: {}", content))?;
    
    // Check if file exists
    let exists = ctx.fs().exists("data/config.json");
    ctx.logger().info("fs", &format!("File exists: {}", exists))?;
    
    Ok(())
}).await

4. Implementing Custom Modules

// Define custom module
struct MyCustomModule {
    name: String,
}

impl MyCustomModule {
    async fn process_data(&self, data: &str) -> DMSCResult<String> {
        // Custom processing logic
        Ok(format!("Processed by {}: {}", self.name, data))
    }
}

// Add custom module when building the application
let custom_module = MyCustomModule {
    name: "MyProcessor".to_string(),
};

let app = DMSCAppBuilder::new()
    .with_config("config.yaml")?
    .with_logging(DMSCLogConfig::default())?
    .with_module(custom_module)?
    .build()?;

// Use custom module in business logic
app.run(|ctx: &DMSCServiceContext| async move {
    // Get custom module
    let processor = ctx.module::<MyCustomModule>()?;
    
    // Use custom functionality
    let result = processor.process_data("sample data").await?;
    ctx.logger().info("custom", &format!("Processing result: {}", result))?;
    
    Ok(())
}).await

Best Practices

  1. Start Simple: Create a basic application first, ensure it runs correctly, then gradually add other modules

  2. Configure Logging Appropriately: Adjust log levels based on environment, use debug for development and info or warn for production

  3. Use Configuration Files: Place configuration information in files, avoiding hardcoding, for easier deployment across different environments

  4. Handle Errors Properly: Use the ? operator to propagate errors, ensuring errors are correctly handled

  5. Design Modularly: Encapsulate business logic in functions or modules to keep code clean

  6. Test Driven Development: Write unit tests and integration tests to ensure code quality

Summary

This example demonstrates how to build a simple DMSC application, including:

  • Project creation and dependency addition
  • Configuration file writing
  • Application building and running
  • Basic functionality usage

Through this example, you should have understood the basic structure and usage of DMSC applications. You can further explore other features of DMSC based on this foundation.

Related Modules

  • README: Usage examples overview, providing quick navigation to all usage examples
  • basic-app: Basic application example, learn how to create and run your first DMSC application
  • authentication: Authentication example, learn JWT, OAuth2, and RBAC authentication and authorization
  • caching: Caching example, understand how to use the cache module to improve application performance
  • database: Database example, learn database connection and query operations
  • grpc: gRPC examples, implement high-performance RPC calls
  • websocket: WebSocket examples, implement real-time bidirectional communication
  • observability: Observability example, monitor application performance and health status
  • validation: Validation example, data validation and sanitization operations

Clone this wiki locally