-
-
Notifications
You must be signed in to change notification settings - Fork 15
Architecture
- Overview
- Project Structure
- Architecture Layers
- Project Details
- Design Patterns
- Integration Flow
- Technology Stack
Servy is a Windows application that lets you run any executable as a Windows service through a simple CLI or GUI interface. It provides a reliable, fully managed solution to automate app startup, monitor running processes, and ensure background execution on Windows platforms ranging from Windows 7 SP1 up to Windows 11 and Windows Server editions.
Designed as an open-source alternative to NSSM, Servy is built entirely in C# for simplicity, transparency, and seamless integration into your workflows and automation pipelines.
The Servy solution consists of six main projects:
Project | Type | Description |
---|---|---|
Servy | WPF Application | Main user interface for service configuration and management |
Servy.CLI | Console Application | Main CLI for service configuration and management |
Servy.Core | Class Library | Shared functionality, utilities, and data models |
Servy.Infrastructure | Class Library | Data access, persistence, and integration with external systems (e.g., SQLite database, configuration files) |
Servy.Service | Windows Service | Windows Service executable that wraps target processes |
Servy.Restarter | Console Application | Service restart utility |
Servy follows Clean Architecture principles, separating responsibilities into distinct tiers for clarity, maintainability, and testability:
┌───────────────────────────────────┐
│ │
│ Presentation Layer │
│ │
│ (Servy Apps - GUI & CLI) │
│ Handles user interaction, input, │
│ and output. Communicates with the │
│ business logic layer. │
│ │
├───────────────────────────────────┤
│ │
│ Business Logic Layer │
│ │
│ (Servy.Core) │
│ Implements the core functionality,│
│ workflows, and service management │
│ rules, independent of UI. │
│ │
├───────────────────────────────────┤
│ │
│ Infrastructure Layer │
│ │
│ (Servy.Infrastructure) │
│ Provides data persistence, access │
│ to the SQLite database, config │
│ management, and external system │
│ integration. │
│ │
├───────────────────────────────────┤
│ │
│ Service Layer │
│ │
│ (Servy.Service) │
│ A Windows Service host that runs │
│ configured apps in the background,│
│ monitors them, and applies health │
│ checks and restart policies. │
│ │
└───────────────────────────────────┘
The diagram below represents Servy’s layered architecture following Clean Architecture principles. At the center is the Core layer (Servy.Core
), which contains the domain entities (Service
) and abstractions/interfaces (IServiceRepository
, IServiceManager
). This layer is independent of external dependencies, ensuring business logic remains decoupled and testable.
The Infrastructure layer (Servy.Infrastructure
) implements the Core interfaces, providing concrete functionality like database persistence (ServiceRepository
), XML/JSON serialization, and password encryption. External services and APIs are only referenced here, not in the Core, following the dependency inversion principle.
The Application or Orchestrator layer (for example, ServiceManager
) coordinates domain operations, calling repositories and services, without containing business rules itself. In Clean Architecture terms, dependencies point inwards toward the Core, ensuring the inner domain remains stable even as infrastructure changes.
This separation allows for flexible testing, easier maintenance, and adaptability, as the domain logic does not rely on concrete implementations or frameworks.
flowchart TB
subgraph Core ["Servy.Core"]
A[Service Domain Model]
B[IServiceRepository Interface]
C[IServiceManager Interface]
end
subgraph Infrastructure ["Servy.Infrastructure"]
D[ServiceRepository Implementation]
E[Xml/Json Serialization Services]
F[SecurePassword Service]
end
subgraph Application ["Application / Orchestrator"]
G[ServiceManager]
end
%% Dependencies
D --> B
E --> B
F --> B
G --> A
G --> B
G --> D
The main WPF application provides the user interface for creating and managing Windows services. The application is built using the MVVM (Model-View-ViewModel) design pattern to ensure clean separation of concerns and maintainable code architecture.
Key Responsibilities:
- Provide user-friendly WPF interface for service configuration
- Handle user input validation
- Communicate with Windows Service Control Manager
- Manage service installation, uninstallation, and configuration
- Handle UAC elevation requests
Key Features:
- Service name & description configuration
- Startup type selection (Automatic, Manual, Disabled)
- Process priority settings (Idle to Real Time)
- Custom working directory and parameters
- Output redirection with log rotation
- Health checks and automatic service recovery
- Environment variables
- Service dependencies
- Monitor and manage services in real-time
- Admin privilege management
classDiagram
direction LR
class App {
+OnStartup(e: StartupEventArgs)
-KillServyServiceIfRunning()
-CopyEmbeddedResource(fileName: string)
-GetEmbeddedResourceLastWriteTime(assembly: Assembly): DateTime
}
class MainWindow {
+MainWindow()
}
class ServiceConfiguration {
+Name: string
+Description: string
+ExecutablePath: string
+StartupDirectory: string
+Parameters: string
+StartupType: ServiceStartType
+Priority: ProcessPriority
+StdoutPath: string
+StderrPath: string
+EnableRotation: bool
+RotationSize: string
+EnableHealthMonitoring: bool
+HeartbeatInterval: string
+MaxFailedChecks: string
+RecoveryAction: RecoveryAction
+MaxRestartAttempts: string
}
class IFileDialogService {
<<interface>>
+OpenExecutable(): string
+OpenFolder(): string
+SaveFile(title: string): string
}
class DesignTimeFileDialogService {
+OpenExecutable(): string
+OpenFolder(): string
+SaveFile(title: string): string
}
class FileDialogService {
+OpenExecutable(): string
+OpenFolder(): string
+SaveFile(title: string): string
}
class IMessageBoxService {
<<interface>>
+ShowInfo(message: string, caption: string)
+ShowWarning(message: string, caption: string)
+ShowError(message: string, caption: string)
}
class MessageBoxService {
+ShowInfo(message: string, caption: string)
+ShowWarning(message: string, caption: string)
+ShowError(message: string, caption: string)
}
class IServiceCommands {
<<interface>>
+InstallService(...)
+UninstallService(serviceName: string)
+StartService(serviceName: string)
+StopService(serviceName: string)
+RestartService(serviceName: string)
}
class ServiceCommands {
-IServiceManager _serviceManager
-IMessageBoxService _messageBoxService
+ServiceCommands(serviceManager: IServiceManager, messageBoxService: IMessageBoxService)
+InstallService(...)
+UninstallService(serviceName: string)
+StartService(serviceName: string)
+StopService(serviceName: string)
+RestartService(serviceName: string)
}
class MainViewModel {
+MainViewModel(fileDialogService: IFileDialogService, serviceCommands: IServiceCommands)
}
App ..> Servy.Service : uses
MainWindow ..> MainViewModel : uses
MainWindow ..> FileDialogService : uses
MainWindow ..> ServiceCommands : uses
MainWindow ..> Servy.Core.Services.ServiceManager : uses
MainWindow ..> Servy.Core.Services.ServiceControllerWrapper : uses
MainWindow ..> Servy.Core.Services.WindowsServiceApi : uses
MainWindow ..> Servy.Core.Services.Win32ErrorProvider : uses
ServiceConfiguration ..> Servy.Core.Enums.ServiceStartType : uses
ServiceConfiguration ..> Servy.Core.Enums.ProcessPriority : uses
ServiceConfiguration ..> Servy.Core.Enums.RecoveryAction : uses
IFileDialogService <|-- DesignTimeFileDialogService
IFileDialogService <|-- FileDialogService
IMessageBoxService <|-- MessageBoxService
IServiceCommands <|-- ServiceCommands
ServiceCommands ..> Servy.Core.Interfaces.IServiceManager : uses
ServiceCommands ..> IMessageBoxService : uses
ServiceCommands ..> Servy.Core.Helpers.Helper : uses
ServiceCommands ..> Servy.Core.Enums.ServiceStartType : uses
ServiceCommands ..> Servy.Core.Enums.ProcessPriority : uses
ServiceCommands ..> Servy.Core.Enums.RecoveryAction : uses
MainViewModel ..> IFileDialogService : uses
MainViewModel ..> IServiceCommands : uses
The Servy CLI provides a text-based interface for advanced users and automation scenarios to create, configure, and manage Windows services. It complements the main WPF application by enabling scripting, CI/CD integration, and headless usage.
Key Responsibilities:
- Configure Windows services via command line parameters and scripts
- Install, uninstall, start, stop, and query services without UI
- Support all service settings (name, description, startup type, priority, working directory, parameters)
- Manage output redirection and log rotation programmatically
- Request UAC elevation when required
- Return meaningful exit codes for scripting automation and error handling
Key Features:
- Full service lifecycle management (install, uninstall, start, stop)
- Service configuration (name, description, startup type: automatic/manual/disabled)
- Process priority adjustment (Idle to Real Time)
- Custom working directory and command-line parameters
- Stdout/stderr redirection with log rotation options
- Health monitoring and automatic service recovery triggers
- Environment variables
- Service dependencies
- Designed for scripting, CI/CD pipelines, and remote management
- Admin privilege detection and elevation support
Note:
The CLI is designed as a lightweight, script-friendly alternative to the WPF interface, focusing on automation and headless scenarios while sharing core service management logic with the GUI application.
classDiagram
class Program {
+static Main(string[] args)
}
class BaseCommand {
#ExecuteWithHandling(Func<CommandResult> action)
#ExecuteWithHandlingAsync(Func<Task<CommandResult>> action)
}
class InstallServiceCommand {
-IServiceManager _serviceManager
-IServiceInstallValidator _validator
+InstallServiceCommand(IServiceManager serviceManager, IServiceInstallValidator validator)
+Execute(InstallServiceOptions opts)
}
class StartServiceCommand {
-IServiceManager _serviceManager
+StartServiceCommand(IServiceManager serviceManager)
+Execute(StartServiceOptions opts)
}
class StopServiceCommand {
-IServiceManager _serviceManager
+StopServiceCommand(IServiceManager serviceManager)
+Execute(StopServiceOptions opts)
}
class RestartServiceCommand {
-IServiceManager _serviceManager
+RestartServiceCommand(IServiceManager serviceManager)
+Execute(RestartServiceOptions opts)
}
class UninstallServiceCommand {
-IServiceManager _serviceManager
+UninstallServiceCommand(IServiceManager serviceManager)
+Execute(UninstallServiceOptions opts)
}
class CommandResult {
+bool Success
+string Message
+static Ok(string message)
+static Fail(string message)
}
class ServiceInstallValidator {
+Validate(InstallServiceOptions opts)
}
class IServiceInstallValidator {
<<interface>>
+Validate(InstallServiceOptions opts)
}
class IServiceManager {
<<interface>>
+InstallService(...)
+StartService(string serviceName)
+StopService(string serviceName)
+RestartService(string serviceName)
+UninstallService(string serviceName)
}
class InstallServiceOptions {
+string ServiceName
+string ServiceDescription
+string ProcessPath
+string StartupDirectory
+string ProcessParameters
+string ServiceStartType
+string ProcessPriority
+string StdoutPath
+string StderrPath
+string RotationSize
+string HeartbeatInterval
+string MaxFailedChecks
+string RecoveryAction
+string MaxRestartAttempts
}
class StartServiceOptions {
+string ServiceName
}
class StopServiceOptions {
+string ServiceName
}
class RestartServiceOptions {
+string ServiceName
}
class UninstallServiceOptions {
+string ServiceName
}
Program --> InstallServiceCommand
Program --> StartServiceCommand
Program --> StopServiceCommand
Program --> RestartServiceCommand
Program --> UninstallServiceCommand
Program --> IServiceInstallValidator
Program --> IServiceManager
InstallServiceCommand --|> BaseCommand
StartServiceCommand --|> BaseCommand
StopServiceCommand --|> BaseCommand
RestartServiceCommand --|> BaseCommand
UninstallServiceCommand --|> BaseCommand
ServiceInstallValidator --|> IServiceInstallValidator
InstallServiceCommand --> IServiceManager
InstallServiceCommand --> IServiceInstallValidator
InstallServiceCommand --> InstallServiceOptions
StartServiceCommand --> IServiceManager
StartServiceCommand --> StartServiceOptions
StopServiceCommand --> IServiceManager
StopServiceCommand --> StopServiceOptions
RestartServiceCommand --> IServiceManager
RestartServiceCommand --> RestartServiceOptions
UninstallServiceCommand --> IServiceManager
UninstallServiceCommand --> UninstallServiceOptions
BaseCommand --> CommandResult
Shared library containing common functionality used across all projects.
Key Responsibilities:
- Implement common utilities and helper classes
- Define interfaces and contracts
Core Components:
-
ServiceManager
- Provides methods to install, uninstall, start, stop, restart, and update Windows services. -
ServiceControllerWrapper
- Defines an abstraction for controlling and monitoring the status of a Windows service. -
WindowsServiceApi
- Provides an abstraction for invoking native Windows Service API functions. -
Win32ErrorProvider
- Provides access to the last Win32 error code. -
RotatingStreamWriter
- Writes text to a file with automatic log rotation based on file size.
classDiagram
direction LR
class ProcessPriority {
<<enum>>
Idle
BelowNormal
Normal
AboveNormal
High
RealTime
}
class RecoveryAction {
<<enum>>
None
RestartService
RestartProcess
RestartComputer
}
class ServiceStartType {
<<enum>>
Automatic
Manual
Disabled
}
class Helper {
+IsValidPath(path: string): bool
+CreateParentDirectory(path: string): bool
+Quote(input: string): string
}
class RotatingStreamWriter {
-FileInfo _file
-StreamWriter _writer
-long _rotationSize
-object _lock
+RotatingStreamWriter(path: string, rotationSizeInBytes: long)
-CreateWriter(): StreamWriter
+WriteLine(line: string)
-GenerateUniqueFileName(basePath: string): string
-Rotate()
+Dispose()
}
class IServiceControllerWrapper {
<<interface>>
+Status: ServiceControllerStatus
+Start()
+Stop()
+Refresh()
+WaitForStatus(desiredStatus: ServiceControllerStatus, timeout: TimeSpan)
+Dispose()
}
class IServiceManager {
<<interface>>
+InstallService(...)
+UninstallService(serviceName: string): bool
+StartService(serviceName: string): bool
+StopService(serviceName: string): bool
+RestartService(serviceName: string): bool
}
class IWin32ErrorProvider {
<<interface>>
+GetLastWin32Error(): int
}
class IWindowsServiceApi {
<<interface>>
+OpenSCManager(...): IntPtr
+CreateService(...): IntPtr
+OpenService(...): IntPtr
+DeleteService(...): bool
+CloseServiceHandle(...): bool
+ControlService(...): bool
+ChangeServiceConfig(...): bool
+ChangeServiceConfig2(...): bool
}
class NativeMethods {
<<static>>
+SERVICE_DESCRIPTION
+SC_MANAGER_ALL_ACCESS: int
+SERVICE_ALL_ACCESS: int
+SERVICE_QUERY_STATUS: int
+SERVICE_DEMAND_START: int
+SERVICE_NO_CHANGE: uint
+SERVICE_CONTROL_STOP: int
+SERVICE_STATUS
+OpenSCManager(...): IntPtr
+CreateService(...): IntPtr
+OpenService(...): IntPtr
+DeleteService(...): bool
+CloseServiceHandle(...): bool
+ControlService(...): bool
+ChangeServiceConfig(...): bool
+ChangeServiceConfig2(...): bool
}
class ServiceControllerWrapper {
-ServiceController _controller
+ServiceControllerWrapper(serviceName: string)
+Status: ServiceControllerStatus
+Start()
+Stop()
+Refresh()
+WaitForStatus(desiredStatus: ServiceControllerStatus, timeout: TimeSpan)
+Dispose()
}
class ServiceManager {
-Func<string, IServiceControllerWrapper> _controllerFactory
-IWindowsServiceApi _windowsServiceApi
-IWin32ErrorProvider _win32ErrorProvider
+ServiceManager(controllerFactory: Func<string, IServiceControllerWrapper>, windowsServiceApi: IWindowsServiceApi, win32ErrorProvider: IWin32ErrorProvider)
+UpdateServiceConfig(...): bool
+SetServiceDescription(serviceHandle: IntPtr, description: string)
+InstallService(...): bool
+UninstallService(serviceName: string): bool
+StartService(serviceName: string): bool
+StopService(serviceName: string): bool
+RestartService(serviceName: string): bool
}
class Win32ErrorProvider {
+GetLastWin32Error(): int
}
IServiceControllerWrapper <|-- ServiceControllerWrapper
IServiceManager <|-- ServiceManager
IWin32ErrorProvider <|-- Win32ErrorProvider
IWindowsServiceApi <|-- NativeMethods
ServiceManager --> IServiceControllerWrapper : uses
ServiceManager --> IWindowsServiceApi : uses
ServiceManager --> IWin32ErrorProvider : uses
ServiceManager --> Helper : uses
ServiceManager --> ProcessPriority : uses
ServiceManager --> RecoveryAction : uses
ServiceManager --> ServiceStartType : uses
ServiceControllerWrapper --> ServiceController : uses
RotatingStreamWriter --> Helper : uses
NativeMethods ..> SERVICE_DESCRIPTION
NativeMethods ..> SERVICE_STATUS
ServiceManager ..> NativeMethods : uses
The Infrastructure Layer is implemented in the Servy.Infrastructure
project.
It is responsible for all data persistence and retrieval operations, as well as integration with external systems such as configuration files or platform APIs.
-
Database Access – Interacts with Servy’s SQLite database (
Servy.db
by default inC:\ProgramData\Servy\db\
). - Data Persistence – Reads and writes service configurations, logs, and related metadata.
-
Configuration Management – Loads and saves application-level settings from
appsettings.json
or.exe.config
. - External Integration – Provides low-level communication with operating system components or external libraries.
Servy.Infrastructure uses Dapper, a lightweight Object–Relational Mapper (ORM) for .NET, to map database rows to strongly-typed C# objects.
Key benefits of using Dapper in Servy:
- Performance – Minimal overhead compared to raw ADO.NET, making it suitable for high-frequency queries.
- Simplicity – Allows writing SQL directly for clarity and control.
- Type Safety – Automatically maps query results to Servy’s DTOs and entity classes.
- Define SQL Queries – Queries are written explicitly in the repository classes.
-
Execute with Dapper –
IDbConnection
is used with Dapper’s extension methods such asQuery<T>()
andExecute()
. -
Return Mapped Objects – Results are returned as domain entities or DTOs to the Business Logic Layer (
Servy.Core
).
using (var connection = new SQLiteConnection(connectionString))
{
return connection.Query<ServiceDto>(
"SELECT * FROM Services WHERE Name = @Name",
new { Name = serviceName }
).FirstOrDefault();
}
By keeping all persistence logic in Servy.Infrastructure
, Servy maintains separation of concerns, ensuring that higher layers (Core, CLI, GUI) remain independent of the underlying storage mechanism.
Windows Service executable that wraps and manages target processes.
Key Responsibilities:
- Act as Windows Service host
- Launch and manage target executables
- Handle process monitoring and restart logic
- Redirect stdout/stderr with rotation
- Apply process priority and working directory settings
- Health checks and automatic service recovery
- Handle service lifecycle operations
Service Lifecycle:
- OnStart - Load configuration and start target process
- OnStop - Gracefully terminate target process
classDiagram
direction LR
class ICommandLineProvider {
<<interface>>
+GetArgs(): string[]
}
class CommandLineProvider {
+GetArgs(): string[]
}
class StartOptionsParser {
<<static>>
+Parse(fullArgs: string[]): StartOptions
}
class ILogger {
<<interface>>
+Info(message: string)
+Warning(message: string)
+Error(message: string, ex: Exception)
}
class EventLogLogger {
-EventLog _eventLog
+EventLogLogger(source: string)
+Info(message: string)
+Warning(message: string)
+Error(message: string, ex: Exception)
}
class NativeMethods {
<<static>>
+CreateJobObject(lpJobAttributes: IntPtr, lpName: string): IntPtr
+SetInformationJobObject(hJob: IntPtr, infoClass: JOBOBJECTINFOCLASS, lpJobObjectInfo: IntPtr, cbJobObjectInfoLength: uint): bool
+AssignProcessToJobObject(hJob: IntPtr, hProcess: IntPtr): bool
+CloseHandle(hObject: IntPtr): bool
+JOBOBJECTINFOCLASS
+LimitFlags
+JOBOBJECT_EXTENDED_LIMIT_INFORMATION
+JOBOBJECT_BASIC_LIMIT_INFORMATION
+IO_COUNTERS
}
class IProcessFactory {
<<interface>>
+Create(startInfo: ProcessStartInfo): IProcessWrapper
}
class IProcessWrapper {
<<interface>>
+ProcessHandle: IntPtr
+OutputDataReceived: event
+ErrorDataReceived: event
+Exited: event
+Id: int
+HasExited: bool
+Handle: IntPtr
+ExitCode: int
+EnableRaisingEvents: bool
+Start()
+Kill()
+WaitForExit(milliseconds: int): bool
+WaitForExit()
+CloseMainWindow(): bool
+MainWindowHandle: IntPtr
+PriorityClass: ProcessPriorityClass
+BeginOutputReadLine()
+BeginErrorReadLine()
+Dispose()
}
class ProcessFactory {
+Create(startInfo: ProcessStartInfo): IProcessWrapper
}
class ProcessWrapper {
-Process _process
+ProcessHandle: IntPtr
+ProcessWrapper(psi: ProcessStartInfo)
+OutputDataReceived: event
+ErrorDataReceived: event
+Exited: event
+Id: int
+HasExited: bool
+Handle: IntPtr
+ExitCode: int
+MainWindowHandle: IntPtr
+EnableRaisingEvents: bool
+PriorityClass: ProcessPriorityClass
+Start()
+Kill()
+WaitForExit(milliseconds: int): bool
+WaitForExit()
+CloseMainWindow(): bool
+BeginOutputReadLine()
+BeginErrorReadLine()
+Dispose()
}
class IServiceHelper {
<<interface>>
+GetServyServiceExecutablePath(): string
+GetServyRestarterExecutablePath(): string
}
class ServiceHelper {
+GetServyServiceExecutablePath(): string
+GetServyRestarterExecutablePath(): string
}
class StartOptions {
+ExecutablePath: string
+ExecutableArgs: string
+WorkingDirectory: string
+Priority: ProcessPriorityClass
+StdOutPath: string
+StdErrPath: string
+RotationSizeInBytes: int
+HeartbeatInterval: int
+MaxFailedChecks: int
+RecoveryAction: RecoveryAction
+ServiceName: string
+MaxRestartAttempts: int
}
class IStreamWriter {
<<interface>>
+WriteLine(line: string)
}
class IStreamWriterFactory {
<<interface>>
+Create(path: string, rotationSizeInBytes: long): IStreamWriter
}
class RotatingStreamWriterAdapter {
-RotatingStreamWriter _writer
+RotatingStreamWriterAdapter(path: string, rotationSizeInBytes: long)
+WriteLine(line: string)
}
class StreamWriterFactory {
+Create(path: string, rotationSizeInBytes: long): IStreamWriter
}
class ITimer {
<<interface>>
+Elapsed: event
+Interval: double
+AutoReset: bool
+Enabled: bool
+Start()
+Stop()
+Dispose()
}
class ITimerFactory {
<<interface>>
+Create(): ITimer
}
class TimerAdapter {
-Timer _timer
+TimerAdapter()
+Elapsed: event
+Interval: double
+AutoReset: bool
+Enabled: bool
+Start()
+Stop()
+Dispose()
}
class TimerFactory {
+Create(): ITimer
}
class IPathValidator {
<<interface>>
+IsValid(path: string): bool
}
class PathValidator {
+IsValid(path: string): bool
}
class Program {
+Main(args: string[])
}
class Service {
-ILogger _logger
-IProcessFactory _processFactory
-IStreamWriterFactory _streamWriterFactory
-ITimerFactory _timerFactory
-IPathValidator _pathValidator
-IProcessWrapper? _process
-ITimer? _heartbeatTimer
-int _failedChecksCount
-StartOptions _options
+Service(logger: ILogger, processFactory: IProcessFactory, streamWriterFactory: IStreamWriterFactory, timerFactory: ITimerFactory, pathValidator: IPathValidator)
+OnStart(args: string[])
+OnStop()
+OnShutdown()
-StartProcess()
-StopProcess()
-HandleProcessExit(sender: object, e: EventArgs)
-HandleHeartbeat(sender: object, e: ElapsedEventArgs)
-LogOutput(sender: object, e: DataReceivedEventArgs)
-LogError(sender: object, e: DataReceivedEventArgs)
-PerformRecoveryAction()
}
ICommandLineProvider <|-- CommandLineProvider
ILogger <|-- EventLogLogger
IProcessFactory <|-- ProcessFactory
IProcessWrapper <|-- ProcessWrapper
IServiceHelper <|-- ServiceHelper
IStreamWriter <|-- RotatingStreamWriterAdapter
IStreamWriterFactory <|-- StreamWriterFactory
ITimer <|-- TimerAdapter
ITimerFactory <|-- TimerFactory
IPathValidator <|-- PathValidator
StartOptionsParser ..> StartOptions : creates
StartOptionsParser ..> RecoveryAction : uses
StartOptionsParser ..> ProcessPriorityClass : uses
Service ..> ILogger : uses
Service ..> IProcessFactory : uses
Service ..> IStreamWriterFactory : uses
Service ..> ITimerFactory : uses
Service ..> IPathValidator : uses
Service ..> StartOptions : uses
Service ..> RecoveryAction : uses
Service ..> ProcessPriorityClass : uses
Program ..> ICommandLineProvider : uses
Program ..> StartOptionsParser : uses
Program ..> ILogger : uses
Program ..> Service : uses
Program ..> IProcessFactory : uses
Program ..> IStreamWriterFactory : uses
Program ..> ITimerFactory : uses
Program ..> IPathValidator : uses
RotatingStreamWriterAdapter ..> Servy.Core.IO.RotatingStreamWriter : uses
NativeMethods ..> JOBOBJECT_EXTENDED_LIMIT_INFORMATION
NativeMethods ..> JOBOBJECT_BASIC_LIMIT_INFORMATION
NativeMethods ..> IO_COUNTERS
ServiceHelper ..> System.Reflection.Assembly : uses
ServiceHelper ..> System.IO.Path : uses
TimerAdapter ..> System.Timers.Timer : uses
PathValidator ..> Servy.Core.Helpers.Helper : uses
Utility component for handling service restart.
classDiagram
direction LR
class IServiceController {
<<interface>>
+Status: ServiceControllerStatus
+WaitForStatus(desiredStatus: ServiceControllerStatus, timeout: TimeSpan)
+Start()
+Stop()
+Dispose()
}
class ServiceController {
-System.ServiceProcess.ServiceController _controller
+ServiceController(serviceName: string)
+Status: ServiceControllerStatus
+Start()
+Stop()
+WaitForStatus(desiredStatus: ServiceControllerStatus, timeout: TimeSpan)
+Dispose()
}
IServiceController <|-- ServiceController
ServiceController --> System.ServiceProcess.ServiceController : wraps
The Servy architecture implements several well-established design patterns:
Pattern | Usage | Location |
---|---|---|
MVVM | Separates the UI (View) from the business logic (Model) using a ViewModel. Extensively used in the Servy UI project for data binding and command handling. |
Servy/ViewModels , Servy/Models , Servy/MainWindow.xaml
|
Factory Method | Used for creating instances of IServiceControllerWrapper , IProcessWrapper , IStreamWriter , ITimer , and Dapper-based repository objects. Decouples client code from concrete implementations. |
Servy.Core/Services/ServiceManager.cs , Servy.Service/ProcessManagement/ProcessFactory.cs , Servy.Service/StreamWriters/StreamWriterFactory.cs , Servy.Service/Timers/TimerFactory.cs , Servy.Infrastructure/Repositories/RepositoryFactory.cs
|
Adapter |
ServiceControllerWrapper adapts System.ServiceProcess.ServiceController to IServiceControllerWrapper . RotatingStreamWriterAdapter adapts Servy.Core.IO.RotatingStreamWriter to IStreamWriter . TimerAdapter adapts System.Timers.Timer to ITimer . Dapper-based repositories adapt raw SQL queries to strongly-typed DTOs. |
Servy.Core/Services/ServiceControllerWrapper.cs , Servy.Service/.../RotatingStreamWriterAdapter.cs , Servy.Service/Timers/TimerAdapter.cs , Servy.Infrastructure/Repositories/DapperRepositoryAdapter.cs
|
Singleton | The StartOptionsParser is a static class, providing a single point of access for parsing command-line arguments. |
Servy.Service/CommandLine/StartOptionsParser.cs |
Strategy | The Service class uses various interfaces (ILogger , IProcessFactory , IStreamWriterFactory , ITimerFactory , IPathValidator ) representing different strategies for logging, process creation, stream writing, timing, and path validation. Implementations can be swapped at runtime. Dapper repositories implement different query strategies depending on the data operation. |
Servy.Service/Service.cs , Servy.Infrastructure/Repositories/*
|
Observer | The IProcessWrapper defines events (OutputDataReceived , ErrorDataReceived , Exited ) that allow other components to observe changes in the process state. |
Servy.Service/ProcessManagement/IProcessWrapper.cs |
Dependency Injection | Constructors of many classes (e.g., ServiceManager , ServiceCommands , Service , repositories) take interfaces as parameters. This promotes loose coupling and testability. |
Throughout Servy.Core , Servy.Service , Servy.Infrastructure , and Servy projects |
Repository | Provides an abstraction over the SQLite database. Encapsulates Dapper queries and commands, allowing the Core and Service layers to operate on strongly-typed objects without knowing SQL details. | Servy.Infrastructure/Repositories/* |
sequenceDiagram
participant User
participant WPF as Servy WPF App
participant Core as Servy.Core
participant SCM as Windows SCM
participant Service as Servy.Service
participant Target as Target Process
participant Restarter as Servy.Restarter
User->>WPF: Configure service
WPF->>Core: Validate configuration
Core->>WPF: Validation result
WPF->>SCM: Install service
SCM->>Service: Create service entry
WPF->>SCM: Start service
SCM->>Service: OnStart()
Service->>Service: Load configuration
Service->>Target: Start process
Service->>Service: Begin monitoring
Service->>Service: Health check
Note over Target: Process runs
Service->>Service: Failure detected
Service->>Target: Restart process
Service->>Restarter: Restart service
- .NET 8.0 - Primary development framework
- .NET Framework 4.8 - Development framework for compatibility with older Windows versions
- WPF (Windows Presentation Foundation) - User interface
- Windows API - Service management and system integration
- Windows Services - Background process hosting
- C# - Primary programming language
- Visual Studio - IDE
- Inno Setup - Installer creation
- Mermaid - Architecture diagrams
- Operating System: Windows 7 SP1 / 8 / 10 / 11 (x64) / Windows Server
- Runtime: .NET 8.0 / .NET Framework 4.8
- Privileges: Administrator privileges (required for service installation)
This architecture documentation is part of the open-source Servy project. Contributions to improve the documentation or codebase are welcome through GitHub issues and pull requests.
For more information about using Servy, see the main README file.
Copyright © Akram El Assas. All rights reserved.