質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

2回答

2017閲覧

RDBMS利用システムの、プログラムコード部分の設計で迷子になっている

ttact

総合スコア170

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

2グッド

1クリップ

投稿2022/03/16 11:41

編集2022/03/17 03:56

概要

RDBMSに接続してデータをストアしたりクエリしたりするアプリケーションを初めてまともに開発しています。Windowsデスクトップアプリです。
DBを使ったシステムの、非テーブルの部分(プログラミングコード部分)について、一般的にどのように設計すべきなのかがよくわかりません。
ググったところ「どうもDAOというのがあるらしい」「サービスがDAOを使うらしい」「DAOはテーブル毎に作る派閥とサービスI/Fに合わせて作る派閥があるらしい」的なことはわかってきたのですが、その先がなかなか進まないので、アドバイスを頂きたく思います。

環境

  • C# / .NET6 / Visual Studio Professional 2022
  • 本番PostgreSQL / ユニットテストSQLite
  • Entity Frameworkは使っていません。今のところIDbConnection等をベタに使って、SQL文を手書きしています。
  • WPF + DryIoc + 自作MVVMライブラリ

現時点での設計方針

  • Data Access Objectというものが永続化(とクエリ?)を担当する。なのでDAOの実実装でSQL文を書く。
  • サービスオブジェクトというものがDAOを操作する。サービスオブジェクトのpublicなI/Fでは、サービス利用側にはRDBMSは意識させない。
  • DIコンテナでアプリケーションを構築したい。なのでDAOのinterfaceとか、サービスのinterfaceとかを定義する。サービスはDAOをinterface経由で操作する。
  • サービスが利用するDAOは、サービスのConstructor引数として注入したい。

疑問

直近で一番困っているのは、IDbConnectionは誰が作って誰が所有し、どう受け渡すのかという点です。
IDbConnectionまたはファクトリメソッド(Func<IDbConnection>)をサービスにConstructor Injectionして、サービスが自身で利用するDAOにsetプロパティで注入する感じでしょうか。そしてサービスのDisposeでIDbConnectionを一緒にDisposeしてあげる感じでしょうか?

ファクトリじゃなくてIDbConnectionの実インスタンスを直接Constructor Injectionするのであれば、誰が接続を担当するのでしょうか?そもそも接続毎にサービスオブジェクトも新しく作るのでしょうか?

当初、サービスやDAOのインターフェースまではRDBMSを意識しないのかな、と思っていたのですが、複数テーブルにまたがるトランザクションの実現でハマってしまいました。こんなスレッドを読んだりして、サービスはRDBMSを意識してもいいのか?DAOが永続化の抽象化レイヤなのだからRDBMSを意識するのはDAOの実実装だけではないのか?と混乱しています。

その他

そもそもEntiryFrameworkも別のフレームワークもなしでベタ実装するのが無謀であるとか、この書籍/webサイトだともっと全体的に勉強できるよとか、ちゃんとデザインパターンがあるよとか、そういう話がありましたら、そちらもご教授頂ければ大変有難く思います。

BluOxy, draq👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2022/03/17 03:11

WPF + Prism + DryIoc とかの特定の環境の話のようですが、そうであれば、それを質問の一行目に書くべきです。
ttact

2022/03/17 03:57

環境に追記しました。 ただ、課題の本質としては、例えばJavaであっても一緒かなと思います。
退会済みユーザー

退会済みユーザー

2022/03/17 04:17

違いますね。環境に合ったやり方、フレームワークに備わったやり方があるはずです。WPF + Prism + DryIoc という環境は分からないので自分はこのスレッドからは撤退します。他の方の回答をお待ちください。
guest

回答2

0

ベストアンサー

DBを使ったシステムの、非テーブルの部分(プログラミングコード部分)について、一般的にどのように設計すべきなのかがよくわかりません。

「一般的に」でしたら、「ソフトウェアアーキテクチャ」に従って設計します。
ただし、ソフトウェアアーキテクチャには種類があるので目的に応じてどれに従うか選定する必要があります。

それが決まると、疑問に記載されている質問については「選定したソフトウェアアーキテクチャに則っているかどうか」の判断軸で良し悪しを判断できるのではないかと思います。

ソフトウェアアーキテクチャの中で比較的簡単に取り入れられるのは、レイヤードアーキテクチャです。
そこに依存性の逆転を取り入れたものがオニオンアーキテクチャです。
他にもクリーンアーキテクチャというものがあったりします。
それぞれの詳しい説明はネットに情報が溢れているため割愛します。

それぞれソフトウェアの設計に対する思想・考え方が違っていますが、どれが最適かはソフトウェアの内容や周りの技術レベルによって変わります。
「現時点での設計方針」を見る限り、オニオンアーキテクチャやクリーンアーキテクチャがそれに当たると思いましたが、いかがでしょうか。

オニオンアーキテクチャをベースで考えるなら、DAOを使うのではなくInfrastructure層にRepositoryクラスを定義してDBの永続化&ドメインオブジェクトの生成を行うべきです。
永続化を行うので、IDbConnectionはRepositoryクラス内部で保持して、IDisposableもこのクラスで行います。

C#

1public class UserRepository : IRepository, IDisposable 2{ 3 private readonly IDbConnection _connection; 4 5 public UserRepository(IDbConnection connection) 6 { 7 _connection = connection; 8 } 9 10 public User Get(int userId) 11 { 12 //TODO: DBからUserオブジェクト生成に必要なデータを取得してreturnする 13 throw new NotImplementedException(); 14 } 15 16 public void Save() 17 { 18 //TODO: DBに保存する処理を記述 19 throw new NotImplementedException(); 20 } 21 22 public void Dispose() => _connection.Dispose(); 23} 24

このようにすることでサービスのような業務ロジックを記述するクラスが、IDbConnection を知らなくて済みます。
依存関係にすると下記のようになります。DI コンテナ側でこの依存関係が表現されるよう実装します。

  • Service references IReposiory
  • Repository references IDbConnection

誰が接続を担当するのでしょうか?

IDbConnection オブジェクトを生成して Repository にインジェクションするのは DI コンテナです
また、IDbConnection, Repository の破棄(Dispose)も DI コンテナの仕事です。

そもそも接続毎にサービスオブジェクトも新しく作るのでしょうか?

それはDIコンテナでインスタンスを登録するときに指定できるはずです。
DryIoc の場合、IContainerRegistry.Register で登録したらそうなりますが、IContainerRegistry.RegisterSingleton で登録したら最初にインジェクションしたオブジェクトが常にインジェクションされるはずです。

最後に

兎にも角にも、コードの設計を考えるならまずは世の中にどのような「ソフトウェアアーキテクチャ」が存在し、それを取り入れる事でどのような目的を果たせるのかを知識として身につけておくと良いでしょう。

投稿2022/03/17 06:13

編集2022/03/17 10:35
BluOxy

総合スコア2663

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ttact

2022/03/17 10:31 編集

BluOxy様、ご回答ありがとうございます。 「クリーンアーキテクチャ」や「オニオンアーキテクチャ」について色々と検索して読んでいたので、対応が遅くなり申し訳ありません。 思い返すと「クリーンアーキテクチャ」等は今までInfoQ等で記事で読んだことはあったのですが、それと今回のようなシステムの設計への繋がりが脳内で全くできていませんでした。 自分が知りたかったことに名前がついたような感じですし、今回のケースにおいてどのような方針で設計すれば良いのか最初の一歩をご提示頂けたので、大変参考になりました。 他の方のために、どんな記事を読んで理解したのか、URLを列挙しておきます。最終的には先頭に示す記事がとても役立ちました。また2番目のものは難易度が高くて内容が最初うまく理解できなかったのですが、きちんと網羅的に説明されており、後で復習するにも参考になると思います: https://qiita.com/nrslib/items/a5f902c4defc83bd46b8 https://gist.github.com/mpppk/609d592f25cab9312654b39f1b357c60 https://hackerslab.aktsk.jp/2020/12/16/214956 https://qiita.com/cocoa-maemae/items/e3f2eabbe0877c2af8d0 https://blog.tai2.net/the_clean_architecture.html
guest

0

本番PostgreSQL / ユニットテストSQLite

本番/テストの使い分けが意味不明ですが、何にせよ ADO.NET + 専用のプロバイダを使うことをお勧めします。

投稿2022/03/16 13:52

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ttact

2022/03/16 22:27

コメントありがとうございます。 私は今はNpgsqlとSystem.Data.SQLiteを使っています。現状はSystem.Data下のインターフェースを通じてDAOをコーディングしていますが、実体としてはNpgsqlConnection/NpgsqlCommandやらSQLiteConnection/SQLiteCommandやらを使っていると思います。なのでプロバイダとしては専用のものを使っているのではないかと思いますが、ご指摘の件はそのような話で合っているでしょうか。 あと、「ADO.NET 入門」でヒットした以下の記事を読んでいます(まだ読み終わってないです) https://atmarkit.itmedia.co.jp/ait/articles/0305/16/news003.html 「ADO.NETを使うべき」というご指摘は、上記ページの中にある図「ADO.NETを構成する.NETデータ・プロバイダと非接続型クラス」にある、「DataSetクラスを中心とする非接続型クラス」を使うべき、という指摘ということでよろしいでしょうか。私の現状は図の上の緑の部分であるXxxConnectionとかXxxCommandとかを直接使っている状態であると認識しております。 > 本番/テストの使い分けが意味不明ですが これは、複数メンバーで開発する際に、各自の開発PC上でDAOのユニットテストをするにあたって、テストコード内でDB及びテストデータを用意するために行っているものです。RDBMSの開発が社内でも初めてなもので、ユニットテストをするにあたってDB及びそのテーブルをどのように構築するのか?各人の開発PCでそれぞれテスト用DBを構築するのか?ユニットテストが環境依存にならないのか?という点が心配で調べたところ、SQLiteで:memory:を使ってテスト用DBを用意する例が見つかりましたので、それを採用しております。
退会済みユーザー

退会済みユーザー

2022/03/17 00:45 編集

> ご指摘の件はそのような話で合っているでしょうか。 基本は参考にされている @IT の記事のようにして、その記事で使っている SqlClient を「NpgsqlとSystem.Data.SQLite」に代えて使っているということであれば回答で書いた「ADO.NET + 専用のプロバイダを使う」ということで合っていると思います。 IDbConnection インターフェースとかを使って、実門者さんの独自実装とか何か特別なことをしているのではないかと誤解してました。 ただし、 > 「ADO.NETを使うべき」というご指摘は、上記ページの中にある図「ADO.NETを構成する.NETデータ・プロバイダと非接続型クラス」にある、「DataSetクラスを中心とする非接続型クラス」を使うべき、という指摘ということでよろしいでしょうか。 ・・・ということを言っているわけではないです。接続型だけを使うのか非接続型も使うかは適材適所です。私の言う ADO.NET とは @IT 記事の図で言うと青色の枠 ADO.NET 全部です。 > 複数メンバーで開発する際に、各自の開発PC上でDAOのユニットテストをするにあたって、テストコード内でDB及びテストデータを用意するために行っているものです。 開発時のテストなどには SQLite を使って、本番には PostgreSQL を使うということですか? それは違いが大きすぎてテストの意味がないと思いますけど。 SQL Server の場合のように、開発時には LocalDB を使って DB の構築やテストを行い、運用に移行する際は接続文字列のみ変更して運用環境の SQL Server に接続するというようなことなら十分意味がありますけど。
退会済みユーザー

退会済みユーザー

2022/03/17 01:10

質問に出てくる DAO というという言葉がまた話をややこしくしているようです。 最初に質問を見て DAO という言葉を目にしたとき、自分は以下を想像してました。(前世紀の遺物的なもので、自分は使ったこともないです) Database オブジェクト (DAO) https://docs.microsoft.com/ja-jp/office/client-developer/access/desktop-database-reference/database-object-dao 質問者さんの言う DAO はそれとは違うもののようですね。 3 階層アーキテクチャ、例えば、プレゼンテーション層 ⇔ ビジネスロジック層 ⇔ データベース層のビジネスロジック層のようなものを言ってますか? .NET Core 3.1 とか .NET 6.0 で利用できる DI 機能を使って「ビジネスロジック層」を DI コンテナに登録し、「プレゼンテーション層」で使いたいということですか?
ttact

2022/03/17 02:06

SurferOnWwwさん、お返事ありがとうございます。 DAOは以下などで示されるものを指しているつもりです。 https://techacademy.jp/magazine/19443 https://www.tutorialspoint.com/design_pattern/data_access_object_pattern.htm 3階層アーキテクチャという言葉も恥ずかしながら初めて知りましたが、 https://www.ibm.com/jp-ja/cloud/learn/three-tier-architecture などを読んだ感じ、私の浅い理解では「サービス」がビジネスロジック本体を、「Data Access Object」がデータベース層(か、DB層に対するインターフェース部分)を指しているように思いました。 DIコンテナに関して:DAO、サービス、サービス利用層(WPFのViewModel)のユニットテストをそれぞれ行っています。サービスのテストでは、DAOのMock(スタブ?)がクエリAPIにてテスト用のデータを直接返すよう組んでいます。本番アプリのDIコンテナには、DAOもサービスもViewModelも登録し、それぞれの上位のコンストラクタで注入するように設計しています。 ADO.NETとDataSetの件については理解しました。接続型か非接続型かを意識して設計し、それに応じて利用するADO.NETのクラスを意識しなければならないのですね。ご教授ありがとうございます。 テストについては、SQL文とか使える機能とかの差が大きすぎるから意味がないということと理解しました。今はまだごくごく基本的なSQLしか使ってないから?問題になってないのだと思います。ユニットテスト用にプログラムやバッチなどでテスト用環境を簡単に構築できないか別途検討したいと思います。。。
ttact

2022/03/17 02:11

すみません、書き漏れです。DIコンテナはDryIocを使っています。 各クラスの依存関係をコンストラクタ引数でインターフェースで注入するように設計実装しております。
退会済みユーザー

退会済みユーザー

2022/03/17 02:41

WPF + Prism + DryIoc とかの特定の環境の話のようですが、そうであれば、それを質問の一行目に書くべきです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問