こんにちは!このドキュメントには、開発の実施方法に関するポリシー、手順、および基準に関する私の個人的な推奨事項が含まれています。
プロジェクトコードは、フォルダーごとのフィーチャーとフォルダーごとに組み合わせて構成する必要があります。
ルートレベルでの意味あなたには、機能を定義するフォルダーがあります。例えば:
|-- common/
|-- authentication/
|-- settings/
Common/Core機能には、デフォルトで任意の機能で使用できるようにするクラス、関数、またはウィジェットが含まれています。
したがって、アイデアは、アプリケーションの機能をトップレベルのフォルダーで分割することです。
すべてのトップレベルフォルダーは、アプリケーションの個別のDART/フラッターパッケージ、依存関係、またはアクセス境界と考えてください。
したがって、共通フォルダーに何かを追加する場合、自問する必要があります。99。%の機能が必要ですか?
あなたがそれを依存関係と考えると、共通の依存関係はあなたのpubspec.yamlで常に宣言されます。
したがって、クラス、関数、またはウィジェットを共通フォルダーに追加する前に、自問してください。
各機能フォルダー内には、タイプで定義されたフォルダーがあります。
ここに、機能フォルダーの一部となるフォルダータイプがあります。
|-- ui/
|-- cubits/
|-- domain_objects/
|-- exceptions/
|-- use_cases
|-- services/
|-- repositories/
|-- data_sources/
|-- dtos/
単一のファイルに関連するファイルが複数ある場合は、ファイル内のクラスのコードを生成するなど、フォルダーにグループ化できます。例えば:
|-- authentication/
|---- cubits/
|------ login/
|-------- login_cubit.dart
|-------- login_cubit.freezed.dart
|-------- login_cubit.g.dart
ユーザーのデバイスインターフェイスに関連するコード、たとえば、UI PageBuilders、UI画面、UIビュー、UIコンポーネント。
画面は、デバイスディスプレイ全体を埋めるユーザーインターフェイスコンポーネントであり、ユーザーインターフェイスビューまたはコンポーネント(ボタン、チェックボックス、画像、ECT)のコンテナです。
また、画面は特定のアプリケーションナビゲーション宛先です。
ガイドライン:
class LoginScreen extends StatelessWidget {
//...fields and constructor...
@override
Widget build ( BuildContext context) {
return Column (
children : < Widget > [
_LoginLogo ,
_LoginForm ,
_LoginActions ,
_LoginFooter
],
);
}
}
// extracted by use case.
class _LoginLogo extends StatelessWidget {}
// extracted by update area.
class _LoginForm extends StatefullWidget {}
// extracted by update area.
class _LoginActions extends StatelesssWidget {}
// extracted by update area.
class _LoginFooter extends StatelessWidget {} login_screen.dart
part 'login_screen_components.dart' ;
class LoginScreen extends StatelessWidget {
//...fields and constructor...
@override
Widget build ( BuildContext context) {
return Column (
children : < Widget > [
_LoginLogo ,
_LoginForm ,
_LoginActions ,
_LoginFooter
],
);
}
}
// Is kept here because it's does not break the 400 max line rule.
class _LoginLogo extends StatelessWidget {} login_screen_components.dart
part of 'login_screen.dart' ;
/// [LoginScreen] 's fields.
class _LoginForm extends StatelessWidget {}
// ************************ Footer ************************
/// [LoginScreen] 's footer.
class _LoginFooter extends StatelessWidget {}
// ************************* ACTIONS *********************************
/// [LoginScreen] 's actions.
class _LoginActions extends StatelessWidget {} class LoginScreen extends StatelessWidget {
@override
Widget build ( BuildContext context) {
return BlocBuilder < LoginCubit , LoginCubitState >(
// Here cubit is not specified either.
builder : ( BuildContext context, LoginCubitState state) {},
);
}
} class SomeWidget extends StatelessWidget {
@override
Widget build ( BuildContext context) {
//...
}
}
class _SubTree1 extends StatelessWidget {}
class _SubTree2 extends StatelessWidget {}
class _SubTree3 extends StatelessWidget {}
class _SubTree4 extends StatelessWidget {} class LoginScreen extends StatelessWidget {
@override
Widget build ( BuildContext context) {
return Column (
childreen : < Widget > [
Button1 (onClick : _openRegisterUser),
Button2 (onClick : _openLogin),
Field (onTextChanged : _onUserNameTextChanged),
],
);
}
void _openRegisterUser () {}
void _openLogin () {}
void _onUserNameTextChanged ( String newText) {}
}ビューは、デバイスディスプレイ全体を埋めず、ユーザーインターフェイスビューまたはコンポーネント(ボタン、チェックボックス、画像、ECT)のコンテナであるユーザーインターフェイスコンポーネントです。
また、ビューはアプリケーションナビゲーションの宛先ではありません。
ガイドライン:
キュービットは、UIのビジネスロジックを含むクラスであり、CubitはUI状態を表す状態に縛られています。
ガイドラインの推奨事項:
Future < String ?> bookAppointment ( BookingData booking);
Future < bool > login ( String username, String password);キュビット状態クラスは、特定の時間に境界を搭載したUIの状態を表しています。
ガイドラインの推奨事項:
class LoginState {
bool logginSuceeded // avoid this.
}ドメインで単一のビジネスオペレーション/目標/ジョブ/タスクを作成するために使用されるクラス。
ガイドライン:
class GetCurrentUserUseCase 、 class SignInUseCase ;UseCaseが必要です。call()が必要です。ここで、返品タイプはユースケースの実行の結果です。Repositories 、 Services 、またはその他のhigh level coordinatorsにのみアクセスする必要があります。DataSourceまたはCubitにアクセスしたり、DTOやその他の低レベルのオブジェクトとの対話をしてはなりません。Cubitまたは他のUseCasesで使用する必要があります。例:
class GetCurrentUserUseCase {
final UserRepository _repository;
const GetCurrentUserUseCase ( this ._repository);
Future < User ?> call () async {
await _repository. getCurrentUser ();
}
} class AuthenticateMemberUseCase {
/// Create a [AuthenticateMemberUseCase] .
const AuthenticateMemberUseCase (
/* constructor arguments */
);
/* ... fields ...*/
/// Execute the use case.
Future < TegTask < void >> call ( MemberAuthenticationCredentials credentials) {
return runTaskSafelyAsync < void >(() async {
final bool isEmailValid = credentials.email.isEmail;
if ( ! isEmailValid) {
throw const TegEmailInvalidException ();
}
final bool isConnected = await _hostDeviceInternetService. isConnected ();
if ( ! isConnected) {
throw const TegInternetUnavailableException ();
}
final String ? deviceId = await _hostDeviceInfoRepository. getDeviceId ();
if (deviceId == null ) {
throw const TegDeviceIdUnavailableException ();
}
final PublicPrivateKeyPair keyPair = await _keyGenerator. generate ();
final Member member = await _authService. signIn (
email : credentials.email,
password : credentials.password,
deviceId : deviceId,
publicKey : keyPair.publicKey,
);
await _updateCurrentMemberUseCase (
member : member,
memberPrivateKey : keyPair.privateKey,
deviceId : deviceId,
);
await _saveMemberAuthenticationCredentialsUseCase (credentials);
final TegTask < List < Account >> memberAccountsTask = await _getMemberAccountsUseCase ();
if (memberAccountsTask.failed) {
throw memberAccountsTask.exception;
}
await _updateMemberCurrentAccountUseCase (
member : member,
deviceId : deviceId,
memberPrivateKey : keyPair.privateKey,
account : memberAccountsTask.result.first,
);
});
}
}特定の機能または機能の範囲に対してCRUD操作のみを使用してデータへのアクセスを提供するクラス、例:
abstract class BookingRepository {
Futute < Booking > getBookingById ( String id);
Future < List < Booking >> getBookings ();
Future < void > deleteBookingById ( String id);
Future < void > saveBooking ( Booking booking);
}ガイドラインの推奨事項:
class DefaultBookingRepository implements BookingRepository {
final LocalBookingDataSource local;
final RemoteBookingDataSource remote;
Future < Booking ?> getBookingById ( String id) async {
Booking ? savedBooking = await local. getBookingById (id);
if (savedBooking == null ) {
Booking ? remoteBooking = await remote. getBookingById (id);
if (remoteBooking != null ) {
await local. saveBooking (remoteBooking);
}
return remoteBooking;
}
return savedBooking;
}
//...other operations...
}インタフェース
abstract class BookingRepository {}実装
class DefaultBookingRepository implements BookingRepository {}
class AnonymousBookingRepository implements BookingRepository {}
class PremiumBookingRepository implements BookingRepository {}たとえば、特定の機能または機能の範囲の機能へのアクセスを提供するクラス:
abstract class AuthenticationService {
Future < void > authenticate ( String username, String password);
//...other functionalities...
}
abstract class AppointmentService {
Future < void > register ( Appointment appointment);
//...other functionalities...
}ガイドラインの推奨事項。
ローカルまたはリモートで配置されたデータへのアクセスを実装するクラス。例
abstract class LocalBookingDataSource {
Futture < void > saveBooking ( Booking booking);
//...other functionalities...
}
abstract class RemoteBookingDataSource {
Future <booking> saveBooking ();
}ガイドラインの推奨事項:
class SQLiteBookingDataSource implements LocalBookingDataSource {
//...............Implementation................
}
class RestApiBookingDataSource implements RemoteBookingDataSource {
//...............Implementation................
}
class MemoryBookingDataSource implements LocalBookingDataSource {
//...............Implementation................
}データ転送オブジェクト - あるシステムから別のシステムにデータを転送するために使用されます。例
class ApiRequest {
//...fields...
}
class ApiResponse {
//...fields...
} class SQLiteTableDefinition {
//...fields...
}ガイドライン:
class ApiRequest {
//...fields...
factory ApiRequest . fromDO ( DOObject doObject) {
//...mapper code...
}
DOObject toDO () {
//...mapper code...
}
}ドメインオブジェクトは、ビジネスの一部またはその中のアイテムのデータクラス表現であり、プラットフォーム、ライブラリ、またはツールとは無関係にビジネスロジックを実行するために使用されます。
ドメインオブジェクトは、たとえば、人、場所、イベント、ビジネスプロセス、または概念と請求書、製品、トランザクション、または人の詳細を表す場合があります。
ガイドライン:
プロジェクトコード構造と同じです。
最初に機能名、次にファイルを含むassetsフォルダー。
例:
|-- authentication/
|---- assets/
|------ password_hidden_icon.svg
|------ forget_password_icon.svg
|------ background.png