Service Definition
A service interface is the basic unit for defining services in a Silky microservice. The interface can be referenced by other microservices, which communicate with its provider via the RPC framework.
Annotate any C# interface with [ServiceRoute] to make it an application service interface:
namespace Demo.Contracts;
[ServiceRoute]
public interface IOrderAppService
{
Task<string> Echo(string ping);
}
By convention, the recommended naming pattern is:
- Interface:
IXxxxAppService→ default route template:api/{appservice}(e.g.api/order) - Implementation:
XxxxAppService
The route template can be customized:
[ServiceRoute("{appservice=customName}")]
public interface IOrderAppService { }
| Property | Description | Default |
|---|---|---|
template | Route template for the service. Use {appservice=name} to set a custom name | api/{appservice} |
Service Entries
A Service Entry is generated for every method defined on a service interface. In Silky, a service entry is analogous to an MVC Action, and the application service is analogous to a Controller.
Each service entry has a corresponding Service Entry ID that uniquely identifies it within the cluster. During RPC communication, a service entry can be located by its ID or by its WebAPI + HTTP verb.
Service Entry ID
ServiceEntryId = FullyQualifiedMethodName + ParameterNames + _ + HttpVerb
Example — the Echo(string ping) method generates:
Test.ITestAppService.Echo.ping_Get
You can view service entry IDs and their configuration in the built-in Dashboard (accessible via the gateway).
Generating WebAPI from Service Entries
When a service interface is referenced by a Web Host or Gateway, Silky generates RESTful WebAPI endpoints based on the interface route template and the HTTP verb attributes on each method.
Rules:
[ProhibitExtranet](or[Governance(ProhibitExtranet = true)]) prevents the endpoint from being accessed externally.[ServiceRoute("{appservice=name}")]sets a custom route prefix for the interface.- The HTTP verb is determined by
[HttpGet],[HttpPost],[HttpPut],[HttpDelete]attributes; the default is inferred from the method name prefix (Get,Create,Update,Delete).
[ServiceRoute]
public interface IOrderAppService
{
// GET api/order/{id}
[HttpGet("{id}")]
Task<OrderOutput> GetAsync(long id);
// POST api/order
[HttpPost]
Task<OrderOutput> CreateAsync(CreateOrderInput input);
// PUT api/order/{id}
[HttpPut("{id}")]
Task<OrderOutput> UpdateAsync(long id, UpdateOrderInput input);
// DELETE api/order/{id}
[HttpDelete("{id}")]
Task DeleteAsync(long id);
// Not exposed externally (RPC only)
[ProhibitExtranet]
Task<OrderOutput> GetInternalAsync(long id);
}
Governance Attribute
Use [Governance] on any service entry method to override the global governance configuration:
[HttpGet("{name}")]
[Governance(ShuntStrategy = ShuntStrategy.HashAlgorithm)]
Task<TestOut> Get([HashKey] string name);
| Property | Description |
|---|---|
ShuntStrategy | Load balancing strategy: Polling, Random, HashAlgorithm |
TimeoutMillSeconds | RPC timeout in milliseconds |
RetryTimes | Number of retry attempts on failure |
EnableCircuitBreaker | Enable/disable circuit breaking |
ProhibitExtranet | Block external (HTTP gateway) access |
MaxConcurrentHandlingCount | Max concurrent RPC requests per instance |
Fallback
Define a fallback implementation for graceful degradation when the remote call fails:
[ServiceRoute]
[Fallback(typeof(OrderAppServiceFallback))]
public interface IOrderAppService
{
Task<OrderOutput> GetAsync(long id);
}
// Fallback implementation
public class OrderAppServiceFallback : IOrderAppService
{
public Task<OrderOutput> GetAsync(long id)
{
// Return a safe default when the remote call fails
return Task.FromResult(new OrderOutput { Id = id, Status = "Unavailable" });
}
}
Service Implementation
Implement the interface in the microservice host project. Silky auto-registers implementations via convention:
// Register as Scoped (one instance per request)
public class OrderAppService : IOrderAppService, IScopedDependency
{
private readonly IOrderRepository _orderRepository;
public OrderAppService(IOrderRepository orderRepository)
{
_orderRepository = orderRepository;
}
public async Task<OrderOutput> GetAsync(long id)
{
var order = await _orderRepository.GetAsync(id);
return order.MapTo<OrderOutput>();
}
}
Supported DI lifetimes via marker interfaces:
ISingletonDependency— singletonIScopedDependency— scoped (one per request)ITransientDependency— transient (new instance each time)
