Preserve explicit model and provider architecture (#30)
This commit is contained in:
1 parent
54f50ad6b7
commit
8b0662e340
10 files changed
+104
-7
No files matched your search
@@ -12,6 +12,13 @@ skill to perform this completion workflow.
|
||||
|
||||
## Preserve the frontend architecture
|
||||
|
||||
This applies to `frontend/app`, every shared package and future web/mobile
|
||||
clients, including in-memory stubs. Keep an explicit data model even when it
|
||||
currently resembles the DTO; do not use a provider as a substitute for that model.
|
||||
The UI depends on providers/models; providers depend on service contracts; service
|
||||
implementations own DTO conversion through mappers. Replacing a service must not
|
||||
require DTO or networking changes in widgets or providers.
|
||||
|
||||
- Strictly preserve the existing `dto` -> `mapper` -> `service` -> `model` ->
|
||||
`provider` flow. Widgets consume models and provider state; they must not
|
||||
bypass the flow by parsing transport data or calling backend APIs directly.
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/// Stub response contracts, replaced by generated transport types at integration.
|
||||
class AuthDTO {
|
||||
const AuthDTO({required this.userId, required this.simulated});
|
||||
final String userId;
|
||||
final bool simulated;
|
||||
}
|
||||
|
||||
class VerificationDTO {
|
||||
const VerificationDTO({required this.simulated});
|
||||
final bool simulated;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import '../dto/results.dart';
|
||||
import '../models/auth.dart';
|
||||
import '../models/verification.dart';
|
||||
|
||||
abstract final class AuthMapper {
|
||||
static AuthResult fromDTO(AuthDTO dto) =>
|
||||
AuthResult(userId: dto.userId, isSimulated: dto.simulated);
|
||||
}
|
||||
|
||||
abstract final class VerificationMapper {
|
||||
static VerificationResult fromDTO(VerificationDTO dto) =>
|
||||
VerificationResult(isSimulated: dto.simulated);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import '../dto/results.dart';
|
||||
import '../mappers/results.dart';
|
||||
import '../models/auth.dart';
|
||||
import 'contracts.dart';
|
||||
import 'verification.dart';
|
||||
@@ -18,7 +20,9 @@ class DummyLoginService implements LoginService, SimulationInfo {
|
||||
Future<AuthResult> login(LoginRequest request) async {
|
||||
await Future<void>.delayed(delay);
|
||||
if (failure case final error?) throw AuthException(error);
|
||||
return const AuthResult(userId: 'demo-user', isSimulated: true);
|
||||
return AuthMapper.fromDTO(
|
||||
const AuthDTO(userId: 'demo-user', simulated: true),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +40,8 @@ class DummySignupService implements SignupService, SimulationInfo {
|
||||
Future<AuthResult> signup(SignupRequest request) async {
|
||||
await Future<void>.delayed(delay);
|
||||
if (failure case final error?) throw AuthException(error);
|
||||
return const AuthResult(userId: 'demo-user', isSimulated: true);
|
||||
return AuthMapper.fromDTO(
|
||||
const AuthDTO(userId: 'demo-user', simulated: true),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import '../dto/results.dart';
|
||||
import '../mappers/results.dart';
|
||||
import '../models/verification.dart';
|
||||
|
||||
abstract interface class SimulationInfo {
|
||||
@@ -30,7 +32,7 @@ class DummyVerificationService implements VerificationService, SimulationInfo {
|
||||
}) async {
|
||||
await Future<void>.delayed(delay);
|
||||
if (sendFailure case final error?) throw VerificationException(error);
|
||||
return const VerificationResult(isSimulated: true);
|
||||
return VerificationMapper.fromDTO(const VerificationDTO(simulated: true));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -38,7 +40,9 @@ class DummyVerificationService implements VerificationService, SimulationInfo {
|
||||
await Future<void>.delayed(delay);
|
||||
switch (token) {
|
||||
case 'demo-valid':
|
||||
return const VerificationResult(isSimulated: true);
|
||||
return VerificationMapper.fromDTO(
|
||||
const VerificationDTO(simulated: true),
|
||||
);
|
||||
case 'demo-expired':
|
||||
throw const VerificationException(VerificationError.expired);
|
||||
case 'demo-internal':
|
||||
|
||||
@@ -204,7 +204,7 @@ class DummyWalletService implements WalletService {
|
||||
@override
|
||||
Future<AccountProfile> profile() async {
|
||||
await _wait();
|
||||
return WalletMapper.profile(_locale);
|
||||
return WalletMapper.profile(ProfileDTO(_locale));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -213,7 +213,7 @@ class DummyWalletService implements WalletService {
|
||||
if (!['en', 'ru'].contains(locale))
|
||||
throw const WalletException(WalletError.unsupported);
|
||||
_locale = locale;
|
||||
return WalletMapper.profile(_locale);
|
||||
return WalletMapper.profile(ProfileDTO(_locale));
|
||||
}
|
||||
|
||||
// Kept separate from wallet fetch because LimitsService has a different result.
|
||||
|
||||
@@ -53,6 +53,11 @@ class OperationDTO {
|
||||
final bool fail;
|
||||
}
|
||||
|
||||
class ProfileDTO {
|
||||
const ProfileDTO(this.locale);
|
||||
final String locale;
|
||||
}
|
||||
|
||||
abstract final class WalletMapper {
|
||||
static CurrencyAccount account(AccountDTO d) => CurrencyAccount(
|
||||
id: d.id,
|
||||
@@ -85,5 +90,5 @@ abstract final class WalletMapper {
|
||||
status: OperationStatus.values.byName(d.status),
|
||||
createdAt: d.created,
|
||||
);
|
||||
static AccountProfile profile(String locale) => AccountProfile(locale);
|
||||
static AccountProfile profile(ProfileDTO dto) => AccountProfile(dto.locale);
|
||||
}
|
||||
Reference in new issue
Block a user