Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Application Service

ASecurity

Define a Protean application service - a stateless orchestration layer that coordinates use cases between external callers (API controllers, CLI handlers, background jobs) and the domain model. Application services load aggregates, invoke domain methods, and persist results without containing business logic themselves. They are always associated with one aggregate via part_of and use the @use_case decorator for automatic UnitOfWork wrapping. Unlike command handlers, application services are i...

45 stars
0 votes
0 copies
0 views
Added 9/20/2026
developmentpythongoapi

Works with

cliapi

Security Analysis

A100/100

Scanned 9/20/2026

Install to Claude Code

$npx -y skills add proteanhq/protean --skill application-service --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Application Service?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Application Service
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/proteanhq-application-service/badge)](https://www.skillsdirectory.com/skills/proteanhq-application-service)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: application-service
description: Define a Protean application service - a stateless orchestration layer that coordinates use cases between external callers (API controllers, CLI handlers, background jobs) and the domain model. Application services load aggregates, invoke domain methods, and persist results without containing business logic themselves. They are always associated with one aggregate via part_of and use the @use_case decorator for automatic UnitOfWork wrapping. Unlike command handlers, application services are invoked directly (not via domain.process()) and always return values synchronously. Use when you need to define a use case, orchestrate a domain operation, create an application service, implement a use-case method, add an entry point for domain operations, wire an API to domain logic in pure DDD (non-CQRS), or when the user asks to "create an application service", "add a use case", "implement an application service", "orchestrate domain operations". Application services are the DDD approach; for CQRS use command handlers instead.
license: Apache-2.0
compatibility: Requires Python 3.11+, protean framework
metadata:
  author: proteanhq
  version: "0.1"
  category: element
---

# Application Service

## Basic structure

```python
from protean import Domain, current_domain, use_case
from protean.fields import Identifier, String

domain = Domain()

@domain.aggregate
class User:
    email: String()
    name: String()
    status: String(default="INACTIVE")

    def activate(self):
        self.status = "ACTIVE"

@domain.application_service(part_of=User)
class UserApplicationServices:
    @use_case
    def register_user(self, email: str, name: str) -> Identifier:
        user = User(email=email, name=name)
        current_domain.repository_for(User).add(user)
        return user.id

    @use_case
    def activate_user(self, user_id: Identifier) -> None:
        user = current_domain.repository_for(User).get(user_id)
        user.activate()
        current_domain.repository_for(User).add(user)
```

## Key rules

1. **part_of is required** — Every application service must be associated with exactly one aggregate: `@domain.application_service(part_of=User)`. If the service is defined in the same file as the aggregate, use a string reference (`part_of="User"`) to avoid circular dependencies
2. **Use @use_case decorator** — Mark each use case method with `@use_case` to get automatic UnitOfWork wrapping. Import from `protean`: `from protean import use_case`
3. **Implicit UnitOfWork** — Each `@use_case` method runs within a UnitOfWork automatically. On success, changes are committed; on exception, everything is rolled back
4. **Direct invocation** — Instantiate and call directly: `svc = UserServices(); svc.register_user(...)`. NOT dispatched via `domain.process()`
5. **Synchronous return values** — Application services always execute synchronously and return values to the caller immediately
6. **Thin orchestration only** — Load aggregate, call domain method, persist result. No business logic in the service; push decisions into aggregates or domain services
7. **One use case per method** — Each method represents a single, cohesive business operation
8. **Persist one aggregate** — A use case method should persist only one aggregate. Use domain events for cross-aggregate consistency
9. **Use case naming** — Name methods after business operations: `register_user`, `place_order`, `cancel_subscription`
10. **No handle_error hook** — Unlike command/event handlers, exceptions propagate directly to the caller. The UnitOfWork rolls back, then the exception re-raises

## Application service options

| Option | Purpose | Required | Default |
|--------|---------|----------|---------|
| `part_of` | Associate service with an aggregate class | Yes | None |

## Quick example: Multiple use cases

```python
@domain.application_service(part_of=Account)
class AccountServices:
    @use_case
    def create_account(self, email: str) -> Identifier:
        account = Account(email=email)
        current_domain.repository_for(Account).add(account)
        return account.id

    @use_case
    def activate_account(self, account_id: Identifier) -> None:
        account = current_domain.repository_for(Account).get(account_id)
        account.activate()
        current_domain.repository_for(Account).add(account)

    @use_case
    def deactivate_account(self, account_id: Identifier) -> None:
        account = current_domain.repository_for(Account).get(account_id)
        account.deactivate()
        current_domain.repository_for(Account).add(account)
```

## Invocation pattern

```python
# Direct instantiation and call (NOT domain.process())
svc = AccountServices()
account_id = svc.create_account(email="user@example.com")
svc.activate_account(account_id=account_id)
```

## The @use_case decorator

The `@use_case` decorator has two responsibilities:

1. **UnitOfWork wrapping** — The method body executes inside a `UnitOfWork` context. Commits on success, rolls back on exception.
2. **Execution logging** — Logs invocation at INFO level for traceability.

Only `@use_case`-decorated methods get UoW treatment. Regular helper methods do not:

```python
@domain.application_service(part_of=Order)
class OrderServices:
    @use_case
    def place_order(self, items: list) -> Identifier:
        # Runs inside UnitOfWork
        order = Order.create(items=items)
        current_domain.repository_for(Order).add(order)
        return order.id

    def _validate_items(self, items):
        # Regular helper - NO UnitOfWork wrapping
        ...
```

## Error handling

Exceptions propagate directly to the caller. The UnitOfWork auto-rolls back:

```python
try:
    svc = UserServices()
    user_id = svc.register_user(email="john@example.com", name="John")
except ValidationError as exc:
    # Handle validation errors (e.g., return 400)
    ...
except Exception as exc:
    # Handle unexpected errors (e.g., return 500)
    ...
```

## Application services vs. command handlers

| Aspect | Application Service | Command Handler |
|--------|-------------------|-----------------|
| Invocation | Direct: `svc.method()` | Via dispatch: `domain.process(cmd)` |
| Return values | Always synchronous | Depends on sync/async mode |
| Error handling | Exceptions propagate to caller | `handle_error` hook available |
| Architecture | Pure DDD | CQRS |
| Input | Plain Python arguments | Command DTO |

When evolving to CQRS, application services are replaced by commands + command handlers.

## Common mistakes

### Missing part_of

```python
@domain.application_service  # Wrong! Missing part_of
class UserServices:
    pass
```

Instead: Always specify part_of with the aggregate class

```python
@domain.application_service(part_of=User)  # Correct!
class UserServices:
    pass
```

### Putting business logic in the service

```python
@use_case
def place_order(self, items: list) -> Identifier:
    if len(items) == 0:
        raise ValidationError("Order must have items")  # Wrong!
    total = sum(i.price * i.quantity for i in items)  # Wrong!
    order = Order(items=items, total=total)
    ...
```

Instead: Push business logic into the aggregate

```python
@use_case
def place_order(self, items: list) -> Identifier:
    order = Order.place(items=items)  # Aggregate enforces rules
    current_domain.repository_for(Order).add(order)
    return order.id
```

### Using domain.process() instead of direct invocation

```python
# Wrong! Application services are not dispatched via domain.process()
domain.process(some_service_call)
```

Instead: Instantiate and call directly

```python
svc = OrderServices()
order_id = svc.place_order(items=[...])
```

### Wrapping in UnitOfWork manually

```python
@use_case
def register_user(self, email, name):
    with UnitOfWork():  # Unnecessary! Already implicit via @use_case
        user = User(email=email, name=name)
        ...
```

Instead: Let `@use_case` handle the UnitOfWork

```python
@use_case
def register_user(self, email, name):
    user = User(email=email, name=name)
    current_domain.repository_for(User).add(user)
    return user.id
```

### Persisting multiple aggregates

```python
@use_case
def transfer_funds(self, from_id, to_id, amount):
    from_acct = current_domain.repository_for(Account).get(from_id)
    to_acct = current_domain.repository_for(Account).get(to_id)
    from_acct.debit(amount)
    to_acct.credit(amount)
    current_domain.repository_for(Account).add(from_acct)
    current_domain.repository_for(Account).add(to_acct)  # Wrong!
```

Instead: Persist one aggregate and use events for cross-aggregate sync

## Detailed references

### Core concepts
- [The @use_case Decorator](references/use-case-decorator.md) - How UnitOfWork wrapping and logging work
- [Unit of Work](references/unit-of-work.md) - Transaction semantics in application services
- [Return Values](references/return-values.md) - Patterns for returning data from use cases
- [Error Handling](references/error-handling.md) - Exception propagation and UoW rollback
- [Anti-patterns](references/anti-patterns.md) - Common mistakes and how to avoid them

### Complete examples
- [Simple Service](assets/application_service_simple.py) - Basic service with one use case
- [Multiple Use Cases](assets/application_service_multiple_use_cases.py) - Service with multiple @use_case methods
- [With Events](assets/application_service_with_events.py) - Aggregate raises events during service operations
- [Return Values](assets/application_service_return_values.py) - Patterns for synchronous return values
- [Error Handling](assets/application_service_error_handling.py) - Exception propagation and rollback

### Related skills
- `aggregate` - Application services orchestrate aggregates
- `command-handler` - CQRS alternative; replaces application services when evolving to CQRS
- `event` - Aggregates may raise events during application service operations
- `repository` - Application services use repositories to load/persist aggregates
- `domain-service` - Cross-aggregate logic invoked from application services

Attribution

proteanhqproteanhq
View sourceMore from proteanhq →
SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Related Skills

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

281612 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2132 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Tanstack Start

Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per ...

9881 votes

Pentest

PTES-aligned adversarial security audit for backend, frontend, and mobile applications. Produces a CVSS-scored Hacker Report with verified PoCs and phased remediation.

5491 votes
View all in development →