新手求助:Windows Service项目如何应用合适的架构模式
Hey there! Great job getting your Windows Service up and running—even if all the code is in one file right now, having a working service is a solid start. Since MVC relies on a view layer that you don't need here, let's look at architecture patterns that fit headless services perfectly.
This pattern is perfect for Windows Services because it splits your code into distinct layers, each with a single responsibility—no view layer required. Here's how to map it to your project:
1. 服务宿主层(你的MyNewService.cs)
This layer should only handle Windows Service lifecycle management—think OnStart, OnStop, and timer triggers. It shouldn't contain any business logic, API calls, or logging code. Its only job is to kick off your core work when the timer fires.
2. 业务逻辑层
This is where your core rules live: "fetch data every hour", "validate API responses", "decide what to log". Extract these into dedicated classes (like DataFetchingService) so they're separate from the service's infrastructure code.
3. 外部依赖层
This layer handles interactions with external systems: calling your API, writing to the event log, or any other outside services. Wrap these in interfaces (like IApiClient, IEventLogWriter) so you can easily swap implementations later (or mock them for testing).
Let's refactor your existing code to fit this structure:
宿主层 (MyNewService.cs)
using System.ServiceProcess; using System.Timers; public class MyNewService : ServiceBase { private Timer _hourlyTimer; private readonly IDataFetchingService _dataFetchingService; // Use dependency injection (DI) for better decoupling (more on this later) public MyNewService(IDataFetchingService dataFetchingService) { _dataFetchingService = dataFetchingService; } protected override void OnStart(string[] args) { // Initialize timer to trigger every hour _hourlyTimer = new Timer(TimeSpan.FromHours(1).TotalMilliseconds); _hourlyTimer.Elapsed += (sender, e) => _dataFetchingService.FetchAndLogData(); _hourlyTimer.Start(); } protected override void OnStop() { _hourlyTimer?.Stop(); _hourlyTimer?.Dispose(); } }
业务逻辑层 (DataFetchingService.cs)
public interface IDataFetchingService { void FetchAndLogData(); } public class DataFetchingService : IDataFetchingService { private readonly IApiClient _apiClient; private readonly IEventLogWriter _eventLogWriter; public DataFetchingService(IApiClient apiClient, IEventLogWriter eventLogWriter) { _apiClient = apiClient; _eventLogWriter = eventLogWriter; } public void FetchAndLogData() { try { var apiResponse = _apiClient.GetLatestData(); // Add your business validation here if (apiResponse.IsValid()) { _eventLogWriter.Write("Successfully fetched data", EventLogEntryType.Information); } else { _eventLogWriter.Write("API returned invalid data", EventLogEntryType.Warning); } } catch (Exception ex) { _eventLogWriter.Write($"Failed to fetch data: {ex.Message}", EventLogEntryType.Error); } } }
外部依赖层
API Client (ApiClient.cs)
public interface IApiClient { ApiResponse GetLatestData(); } public class ApiClient : IApiClient { private readonly HttpClient _httpClient; public ApiClient(HttpClient httpClient) { _httpClient = httpClient; _httpClient.BaseAddress = new Uri("https://your-api-endpoint.com/"); } public async Task<ApiResponse> GetLatestData() { var response = await _httpClient.GetAsync("data"); response.EnsureSuccessStatusCode(); return await response.Content.ReadFromJsonAsync<ApiResponse>(); } }
Event Log Writer (EventLogWriter.cs)
using System.Diagnostics; public interface IEventLogWriter { void Write(string message, EventLogEntryType entryType); } public class EventLogWriter : IEventLogWriter { private readonly EventLog _eventLog; public EventLogWriter() { _eventLog = new EventLog(); if (!EventLog.SourceExists("MyNewService")) { EventLog.CreateEventSource("MyNewService", "Application"); } _eventLog.Source = "MyNewService"; } public void Write(string message, EventLogEntryType entryType) { _eventLog.WriteEntry(message, entryType); } }
- Use Dependency Injection: Tools like
Microsoft.Extensions.DependencyInjection(built into .NET) or Autofac will handle creating instances of your services and injecting dependencies. This makes testing easier—you can mockIApiClientin tests instead of calling a real API. - Keep Layers Separate: Never put API calls or logging logic in the service host. If you need to change your API endpoint or switch to a different logging system later, you only have to modify one layer.
- This aligns with MVC principles: Think of the service host as the "controller" (it triggers actions), the business logic as the "model" (handles core rules), and since there's no UI, you just drop the view layer entirely. The core idea of separation of concerns is the same!
内容的提问来源于stack exchange,提问作者Deathibrylator

