.github/skills/implement-domain/SKILL.md
Implements the Flutter domain layer (Entities and Repository Interfaces) following Clean Architecture. Use whenever creating or modifying files in lib/domain/**, defining a new business concept, modeling a domain object, creating a contract between layers, or adding fields to an existing entity. Covers @immutable Entities with copyWith/==/hashCode, Repository Interfaces with Result<T>, and anti-patterns to avoid. Activate even when the user says "create a class for the user data", "add an entity", "model the product", or "define the contract for the repository" without explicitly mentioning domain layer or Clean Architecture.
npx skillsauth add andrelucassvt/CleanMacForDevsWeb implement-domainInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
@immutable, construtor const, propriedades final, copyWith(), == e hashCode.abstract class, retorne Future<Result<T>> em todos os métodos, use Entities (nunca Models) nos parâmetros e retornos.listEquals do package:flutter/foundation.dart.lib/domain/
├── entities/
│ ├── user_entity.dart
│ ├── product_entity.dart
│ └── home_entity.dart
└── interfaces/
├── user_repository.dart
├── product_repository.dart
└── home_repository.dart
Nomenclatura:
<nome>_entity.dart<nome>_repository.dartimport 'package:flutter/foundation.dart';
@immutable
class UserEntity {
const UserEntity({
required this.id,
required this.name,
required this.email,
});
final String id;
final String name;
final String email;
UserEntity copyWith({
String? id,
String? name,
String? email,
}) {
return UserEntity(
id: id ?? this.id,
name: name ?? this.name,
email: email ?? this.email,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is UserEntity &&
runtimeType == other.runtimeType &&
id == other.id &&
name == other.name &&
email == other.email;
@override
int get hashCode => Object.hash(id, name, email);
@override
String toString() => 'UserEntity(id: $id, name: $name, email: $email)';
}
@immutable — SEMPRE adicioneconst constructorfinal em todos os camposcopyWith() — SEMPRE implemente== e hashCode — SEMPRE implementetoString() — recomendado para debuggingimport 'package:flutter/foundation.dart';
@immutable
class HomeEntity {
const HomeEntity({
required this.message,
required this.items,
});
final String message;
final List<String> items;
HomeEntity copyWith({String? message, List<String>? items}) {
return HomeEntity(
message: message ?? this.message,
items: items ?? this.items,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is HomeEntity &&
message == other.message &&
listEquals(items, other.items); // ✅ Use listEquals
@override
int get hashCode => Object.hash(message, items);
}
@immutable
class UserEntity {
const UserEntity({
required this.id,
required this.name,
required this.address, // ✅ Entity composta
});
final String id;
final String name;
final AddressEntity address;
UserEntity copyWith({String? id, String? name, AddressEntity? address}) {
return UserEntity(
id: id ?? this.id,
name: name ?? this.name,
address: address ?? this.address,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is UserEntity &&
id == other.id &&
name == other.name &&
address == other.address; // ✅ Compara entity interna
@override
int get hashCode => Object.hash(id, name, address);
}
import 'package:base_app/config/error/result_pattern.dart';
import 'package:base_app/domain/entities/user_entity.dart';
abstract class UserRepository {
Future<Result<UserEntity>> getUserById(String id);
Future<Result<List<UserEntity>>> getAllUsers();
Future<Result<UserEntity>> createUser(UserEntity user);
Future<Result<UserEntity>> updateUser(UserEntity user);
Future<Result<void>> deleteUser(String id);
}
@immutable
class PageResult<T> {
const PageResult({
required this.items,
required this.currentPage,
required this.totalPages,
required this.totalItems,
});
final List<T> items;
final int currentPage;
final int totalPages;
final int totalItems;
}
abstract class ProductRepository {
Future<Result<PageResult<ProductEntity>>> getProductsPaginated({
required int page,
required int pageSize,
});
}
abstract classFuture<Result<T>>lib/domain/entities/<nome>_entity.dart@immutableconstfinalcopyWith()== e hashCodetoString() (recomendado)listEquals para listaslib/domain/interfaces/<nome>_repository.dartabstractFuture<Result<T>>result_pattern.dart| Erro | Correto |
|---|---|
| String name; sem final | final String name; |
| Sem @immutable e const | @immutable class X { const X({required this.name}); } |
| Sem copyWith() | Implementar com X? name e name ?? this.name |
| Future<UserModel> getUser() | Future<Result<UserEntity>> getUser() |
| import 'package:dio/dio.dart' no domain | Apenas flutter/foundation.dart e imports do próprio projeto |
| Repository sem Result<T> — lança exceções | Future<Result<UserEntity>> getUser(String id) |
Última atualização: 28 de março de 2026
testing
Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy. Activate even when the user says 'create a skill for X', 'the skill is not triggering', 'improve this skill description', 'the agent is not using the skill', 'add a skill to teach the agent how to do X', 'this skill is wrong', or 'update the skill' without explicitly mentioning evals or benchmark.
development
Implements Flutter reusable widgets following the project architecture. Use whenever creating or modifying widgets in presentation/<feature>/widgets/, presentation/<feature>/content/, or common/widgets/. Covers StatelessWidget vs StatefulWidget decision, Entity as parameter, i18n, dispose, componentization rules, and when to access the Cubit via context.read. Activate even when the user says 'extract this to a widget', 'create a list item widget', 'build a reusable card', 'factor out this UI block', 'create a component for this', or 'this View is getting too big' without explicitly mentioning StatelessWidget or reusable components.
tools
Implements Flutter View screens following the project architecture. Use whenever creating or modifying a View (StatefulWidget + Cubit + BlocBuilder), adding a new screen, wiring up BlocBuilder/BlocConsumer/BlocListener, setting up SafeArea, or navigating from the View. Covers State, Cubit, View file, route, DI registration, and common mistakes. Activate even when the user just says "create a screen" or "add a new page", without explicitly mentioning Cubit or BLoC.
testing
Implements Flutter Cubit and State (View Model layer) following the project architecture. Use whenever creating or modifying a Cubit or State class, adding an async method to a Cubit, handling form submission or validation, implementing debounce search, managing loading/error/navigation states, or wiring a Cubit to a Repository or StorageService. Covers sealed States, async patterns with Result<T>, CRUD Cubits, local persistence via StorageService, navigation states, debounce, and common mistakes. Activate even when the user says "add a method", "handle the loading state", or "save locally" without explicitly mentioning Cubit or BLoC.