こまぶろ

技術のこととか仕事のこととか。

環境構築なしでTDDを楽しむ:cyber-dojoのご紹介

新しい言語をちょっと触ってみたいな、でもIDEとか整えるのはハードルが高いな、というときは、cyber-dojoで試してみることが多い。

f:id:ky_yk_d:20190126144310p:plain
「cyber-dojo」のトップページ

様々な言語でTDDを試せる「cyber-dojo

cyber-dojoは、Webブラウザで利用できるコードエディタ&実行環境で、様々な言語・テスティングフレームワーク、様々なお題でテスト駆動開発を楽しむことができる。

f:id:ky_yk_d:20190126144315p:plain:w700
様々な言語・テスティングフレームワークが選べる

使用したい言語とテスティングフレームワークを選ぶと、実装コードとテストコードのサンプルが用意された状態で開始される。お題を(FizzBuzzなど)を選ぶようにはなっているが、選んだお題に応じて、テストコードが用意されるということはない。したがって、cyber−dojo側で用意されている以外のお題で開発をすることも可能である。

サンプル:ClojureFizzBuzz

サンプルとして、ClojureFizzBuzzを書いてみた。

f:id:ky_yk_d:20190127004527p:plain

ソースコードをは以下の通り。

(ns fizzbuzz)

(defn fizzbuzz [n]
  (cond (= 0 (mod n 15)) "FizzBuzz"
        (= 0 (mod n 3)) "Fizz"
        (= 0 (mod n 5)) "Buzz"
        :else n ))
(ns fizzbuzz-test
  (:require [clojure.test :refer :all]
            [fizzbuzz :refer :all]))

(deftest fizzbuzz-test
  (testing "FizzBuzzでは"
    (testing "3の倍数でも5の倍数でもないとき、その数字を言う"
      (are [n] (= n (fizzbuzz n)) 1 2))
    (testing "3の倍数のとき、Fizzと言う" 
      (are [n] (= "Fizz" (fizzbuzz n)) 3 6 9 12 -3))
    (testing "5の倍数のとき、Buzzと言う"
      (are [n] (= "Buzz" (fizzbuzz n)) 5 10 20 -5))
    (testing "15の倍数のとき、FizzBuzzと言う"
      (are [n] (= "FizzBuzz" (fizzbuzz n)) 15 30 45 -15))
    )
  )

どんなときに使うか

cyber−dojoは、様々な用途に使うことができる。上のClojureの例がそうであるように、新しい言語を触ってみたいときには、開発環境を用意しなくてもいいし、テストコードのサンプルも見ることができるので便利だ。

また、TDDのワークショップにも便利だ。上で紹介したように単独のユーザーで利用し、同じ画面を複数人で見るモブプログラミング方式で利用することもできるし、グループセッション機能を使えば複数の参加者のコードをチューターが見るというような利用の仕方もできる。

cyber−dojoはブラウザだけで動作するから、参加者全員分の環境を整える必要はないし、参加者が急に増えたときにもPCさえあれば対応できる。限られた時間でワークショップを行いたい時などは、非常に便利だろう。

終わりに

新しい言語を学ぶときは、環境構築がネックになることが非常に多い。cyber−dojoは、そういった際の強い味方になるだろう。また、 言語におけるテストコードの書き方も同時に学ぶことも、実際の業務にその言語を生かそうとするのであれば有意義なことだろう。

プログラミングClojure 第2版

プログラミングClojure 第2版

リレーショナルモデルにおける制約とCQRS・SQLQL

前回の記事で、リレーショナルデータベースの役割として、以下の3つを挙げ、ドメイン駆動設計においては最後のものにあまり重きが置かれていないことを指摘した。

  • 集合演算によるリレーションの導出(検索)
  • ディスクへの書き込みによる永続化(蓄積)
  • 各種制約によるデータの整合性の確保

ky-yk-d.hatenablog.com

本記事では、リレーショナルデータベースの特性を改めて検討し、その特性からの帰結としてCQRSとSQLQLを位置付けてみたい。

リレーショナルモデルにおける整合性の確保

整合性の確保をリレーショナルデータベースが担うという発想は、決して新奇なものではない。リレーショナルモデルの父であるコッドの1970年の論文「A Relational Model of Data for Large Data Banks」*1でも、データの整合性(consistency)というテーマが大きく取り上げられており、それを維持するための制約宣言文(constraint statement)を定義するというアイデアも提起されている。

現実のリレーショナルデータベースにおいても、制約は確かに存在している。一意性制約や参照整合性制約、チェック制約などがそれである。また、制約という呼び方は通常しないが、トリガもまた、データの整合性を守る仕組みとして利用できる。『リレーショナルデータベース入門』では、以下のように記載されている*2

これらの制約を表現するためのツールは、現実のリレーショナルデータベースにおいては限定的な実装を受けるに留まっている。それにはパフォーマンスの問題も関係しているだろう*3。運用上も、制約というデータベースの定義の変更は大規模なロックを伴うから*4、ビジネスルールをデータベースの制約の側に大きく寄せることはリスクが大きい。現実のアーキテクチャは、現実のリレーショナルデータベースに基づいて構築されなければならないし、構築されているだろう。次に、リレーショナルデータベースの特性に応じたアーキテクチャについて考えてみる。

リレーショナルデータベースの特性から考えるCQRS

制約は、更新時にどのようにデータの整合性を守るかを関心事としている。これは、実際のリレーショナルデータベースにおいては、参照整合性制約などの限られた形で実装されるに止まっている。その一方で、参照を行うための言語であるデータサブ言語、その実装としてのSQL(のSELECT文)はかなり発達している。

以上のような事情から、リレーショナルデータベースは、参照には柔軟に対応できる(対応しても危険が少ない)のに対し、更新には柔軟に対応できない(対応すると整合性が守れない)という特性を持っている。更新の操作を提供する場合には、リレーショナルデータベース側では担えない制約を一枚外側のレイヤ(具体的にはアプリケーションのコード)でかけておく必要が生じる。

前回の記事で指摘したように、『エリック・エヴァンスのドメイン駆動設計』においては、この「更新には柔軟に対応できない」というリレーショナルデータベースの特性を補うものとして、ドメイン層のクラスが役割を果たす。具体的には、ドメインモデル上の集約に不変条件を結び付け、トランザクション整合性の単位として厳格に守るような工夫をするのである。

一方で、参照であれば、データの整合性を維持する役割をアプリケーションが担う必要もそれほどない*5。このことをアーキテクチャに反映させたのが、『エリック・エヴァンスのドメイン駆動設計』(2004年)から9年後に描かれた『実践ドメイン駆動設計』(2013年)で大きく取り上げられているCQRS(コマンド/クエリ責務分離)だと言える。


CQRSにおいては、書き込み系と読み取り系とでモデルを使い分ける。業務ルールに基づくデータの整合性に配慮すべき書き込み系のモデル(ドメインモデル、ライトモデル)から読み取り系のモデル(クエリモデル、リードモデル)を切り離すことができるというのが、そのポイントになる。

CQRSの着眼点は、書き込み系と読み込み系とではアプリケーションに対する要求が異なるということであり、必ずしもリレーショナルデータベースの特性に着目したものではない。しかし、先に指摘したリレーショナルデータベースの特性から考えてみると、自然な帰結としてCQRSを導くこともできたのではないだろうか。

リレーショナルデータベースの強みを活かす「SQLQL」

リレーショナルデータベースの特性からCQRSを導くとすれば、その先には「ユーザから直接SQLを受け付ける」という発想が見えてくる。yanchaさんが提案している「SQLQL」がそれである*6。SQLQLというのは、リモートのエンドポイントに対して、クライアントからSQLを文字列として送信することによってJSON形式の文字列として実行結果を得る仕組みを表している。

qiita.com

リレーショナルデータベースは、それ自体として柔軟な参照の仕組みを提供している。そうであるとすれば、

  • クライアントのアプリケーション
  • サーバのアプリケーション
  • リレーショナルデータベース

という構成をとった場合に、サーバのアプリケーションで一旦オブジェクトとして取り扱うことが、リレーショナルデータベースの柔軟さを殺すことになっているという解釈もできる*7

CQRSを採用するだけでは、上で指摘した事態は解消されない。参照側で、リードモデルとして静的なクラスを定義している場合はデータ構造に修正を施す必要があるし、データ構造を動的に生成している場合であっても、参照の仕方を変えるためにはサーバ側のクエリに手を入れる必要がある。

このような状況に対し、SQLQLを採用すれば、画面での参照の仕方を変えたい、欲しい属性が増えたという場合に、サーバのアプリケーションには一切修正を入れることなく対応できるわけである。柔軟な参照の仕組みを提供しているリレーショナルデータベースの能力を最大限に活かす考え方だと言えるだろう*8

終わりに

以上、参照は柔軟だが更新は柔軟でない(柔軟ではいけない)というリレーショナルデータベースの特性の自然な帰結として、CQRSとSQLQLを位置付けることを試みた。CQRSもSQLQLも他人の考えたものであるが、自分なりの整理ができたのではないかと思う。

本記事でできなかったことを挙げておく。まず、オブジェクト指向データベースやキーバリューストアなどのNoSQLデータベースには詳しくないので、それらとの比較はできなかった。また、大きな課題として、リレーショナルモデルとオブジェクト指向モデルの違いについても、検討が及んでいない。

オブジェクト指向には、単なるデータ構造としての意味だけでなく、振る舞いを持たせるという側面、あるいは人間のメンタルモデルとの一致を目指すという側面もある*9。今後の課題としたい。

*1:杉本啓さんによる対訳版を使用した。こちらの勉強会のページに原文および対訳版へのリンクがある。

*2:増永良文『リレーショナルデータベース入門』44ページ。ただし、ここでは整合性(consistency)ではなく「一貫性(integrity)」という表現が用いられている。両者の関係については、Quoraの回答が手がかりになる。

*3:コッドの1970年論文でも、整合性の検査が更新操作の速度を落としてしまうことへの懸念が述べられているとともに、対処するためのアプローチとしてバッチによる検査実行が挙げられている。

*4:データベースにおける制約の過度な利用については、『失敗に学ぶRDBの歩き方』に記載がある。それを読んでいた時の自分のツイートも参照。

*5:もちろん、柔軟に発行できる問い合わせのなかで「意味のある」ものだけを提供するという意味では、参照側もアプリケーションには意味がある。しかしそのアプリケーションは操作を隠蔽するだけで独自の操作を行うものではなく、ラッパー以上のものではないのではないかと思われる。

*6:SQLQLという概念の存在は、すえなみ(@a_suenami)さんに教えていただいた。

*7:「意味のある」参照のみを提供するということはもちろんある。先の注釈を参照。

*8:とはいえ、セキュリティなどの面でまだ実装上の課題は多いようだ。

*9:メンタルモデルとの一致という観点から、オブジェクト指向に対してDCIからの批判がある。この点については、「DCIアーキテクチャ - Trygve Reenskaug and James O. Coplien」を参照のこと。

『エリック・エヴァンスのドメイン駆動設計』におけるリレーショナルデータベース

近頃、以下に挙げるようなイベントで話を聞いたり議論をしたりする中で、オブジェクト指向とリレーショナルモデルについて思いを巡らせている。

今回は、その中間報告として、『エリック・エヴァンスのドメイン駆動設計』におけるリレーショナルデータベース*1について考えたことを一度まとめておく。

永続化ストアとしてのリレーショナルデータベース

ドメイン駆動設計の原典である『エリック・エヴァンスのドメイン駆動設計』(2004年)において、リレーショナルデータベースは主要な永続化ストアの地位を占めている。

第6章「ドメインオブジェクトのライフサイクル」において、ライフサイクルの中期にあるドメインオブジェクトの格納先/再構成に関わる要素としてリポジトリが紹介される。リポジトリは、何らかの永続化ストアとのやりとりを担うドメインオブジェクトである。このやりとりの相手、つまりライフサイクル中期にあるオブジェクトの永続化先として、エヴァンスは複数の種類の永続化ストアを挙げている。しかし書籍において、永続化ストアのなかでもリレーショナルデータベースは特別に手厚い言及を受けている。

リレーショナルデータベースにオブジェクトを格納する際に考慮すべき事柄として、エヴァンスは以下のように述べている。

・データベース*2をオブジェクトの格納先として見る場合には、マッピングツールの能力に関わらず、データモデルとオブジェクトモデルをかけ離れたものにしてはならない。関係モデルと近づけておくため、オブジェクトの関係性が持つ豊かさを若干犠牲にすること。オブジェクトとのマッピングを単純化するのに役立つなら、正規化のような、関係モデルが持つ正式な標準に関して、ある程度は妥協すること。
・オブジェクトシステムの外部にあるプロセスからは、そうしたオブジェクトの格納先にアクセスしてはならない。オブジェクトが強制する不変条件に違反する可能性があるからだ。また、そうしたアクセスがあるとデータモデルが固定化されてしまい、オブジェクトをリファクタリングする際に、変更するのが難しくなる。*3

上記の引用箇所のうち、前半部分ではオブジェクトモデルとリレーショナルモデルの双方を妥協させるべきであるということが述べられている。マッピングツールが優秀であれば、永続化ストア上の形式とアプリケーション上の形式とが乖離していても技術的には困らないが、エヴァンスは複数のモデルの分離を強く警戒して、モデリングをなるべく近づけるように提唱している。

リレーショナルデータベースもまた、モデルが表現されるべき場所なのであり、オブジェクトに一方的に譲るような関係性ではない。オブジェクトの表現力を最大限活用することよりも、モデルの統一性を重視しているという事実は、ドメイン駆動設計の何たるかを考える上でも重要ではないだろうか。

「不変条件の強制」という役割の担い手

一方、後半部分では、オブジェクトが(リレーションには与えられない)特別な役割を担わされている。それは、「不変条件の強制」である。不変条件の強制は、オブジェクト(とりわけ集約)が担うものであって、リレーショナルデータベースに期待されているのは格納したオブジェクトを外部から守ることである。

データが満たすべき一定の条件の記述は、必ずしもオブジェクトの専売特許ではない。リレーショナルデータベースにおいても、CHECK制約や参照整合性制約など、さまざな制約を記述することができる。実際のRDBMS製品においてはあまりサポートされていないが、複数のテーブルをまたいだかなり複雑な制約も関係演算を利用して記述できるASSERTION(表明)というものも標準SQLには存在している。

リレーショナルデータベースの機能として、データベースの主要な機能である検索と蓄積に対応した2点、すなわち、

  • 集合演算によるリレーションの導出(検索)
  • ディスクへの書き込みによる永続化(蓄積)

に加えて、3番目のものとして、

  • 各種制約によるデータの整合性の確保

挙げるのは不当ではないと思うが、その役割はドメイン駆動設計においてはリレーショナルデータベースにはあまり期待されていないのである。

業務のルールの一部である不変条件を、オブジェクトが表現するというのは、ドメイン駆動設計にとっては重要な事柄だろう。リレーショナルデータベースに不変条件の強制を担わせるのは、「ドメイン知識の流出」につながる。業務ロジックとしてのチェックに加えてクライアントのUIでの入力チェックを行うことが否定されないのと同様に、重畳的にリレーショナルデータベース側でも不変条件を強制することは許されるだろうが、リレーショナルデータベースでしか表現されない業務ルールの存在は忌避の対象であろう。

DOMAINやASSERTIONが広くサポートされていたとしても、リレーショナルデータベースはドメイン知識を表現する場所になれたかどうかはわからない。しかし、リレーショナルデータベースにおける制約は宣言的に記述されるものであるから、大きなポテンシャルを持っているもののように思えてならない。

 コッド博士は、冗長性を制約ととらえて、それを満たしている状態を、一貫性がある状態と定義する。こうした定義は若干狭いように思う。例えば部品表では、完成品の親部品は存在してはならないという制約がある筈だが、これは冗長性とは関係が無い。冗長性と制約はイコールではないと思う。
 一方で、データに関する制約を(手続き的にではなく)宣言的に記述するというアイデアは先進的だったし、40年以上経った現在でも部分的にしか実現されていない。*4

おわりに

だいぶまとまってきたつもりだったのだが、いざ文章を書き出してみるとまだまだ理解が足りないことを感じる。記事になりそうなネタは育ってきてはいるので、連休中にあと1、2本くらい書きたいと思う。

*1:リレーショナルモデルとリレーショナルデータベースの区別を明晰に語るには時期が早いと感じたので、以下ではリレーショナルデータベースを議論の対象とする。

*2:この引用箇所は「関係データベースに合わせてオブジェクトを設計する」という項目の一部であるから、データベースとはリレーショナルデータベースのことを指しているとみて間違いないだろう。

*3:『エリック・エヴァンスのドメイン駆動設計』160ページ。強調は引用者。

*4:Coddの1970年論文、"A Relational Model of Data for Large Shared Data Banks"に対する杉本啓さんによる訳注より。

関連テーブルへの操作と、ドメイン駆動設計における集約・リポジトリ

ドメイン駆動設計を意識しながら設計をしている*1ときに、関連テーブルの操作に関して悩んでいたことがあった。人に相談に乗ってもらい、自分でも改めて書籍などを見ながら考え直したところ、自分の集約への理解が全く不十分であったことがわかった。

TL;DR

問い:

ある集約への変更のために、関連テーブルへの操作を実体のテーブルへの操作と別に提供したくなったらどうする?

回答:

集約の単位とは独立に提供したくなるのは集約をちゃんと考えられていないからかもしれないので考え直そう。

前提

「部署」と「社員」という2つの実体*2の間に、「所属」という多対多の関連が存在しているとする。部署は部員として複数の社員を持つことができる一方で、社員は所属先として複数の部署を持つことができる(つまり、兼部を許す)。「所属」を連関エンティティとすることで、以下のようなデータベース設計になっているとする。

このようなデータの保存形式をとっている部署に対する、以下のようなユースケースをサポートしたい。DB上のデータ操作も併せて記載する。

部署への操作 操作対象テーブル データ操作
名称を変更する 部署テーブル UPDATE 部署名カラムを更新する
新たな社員を配属する 所属テーブル INSERT 新たなレコードを追加する

問い:

以上の操作を提供するために、データベースアクセス手段をどのように提供するべきなのか。

悩みと相談

集約の単位として部署を用いるのであれば、部署リポジトリのメソッド、たとえばsave()で両方をまとめて実行するというのがセオリーだろう。しかし、操作対象のテーブルが異なっており、ユースケースとしても独立しているという理解であったため、あえてドメイン層で束ねる意味があるのだろうか、という疑念が頭から離れない。

先日参加した勉強会の懇親会で、n.sienaさんとkabukawaさんがいらっしゃったので、疑問をぶつけてみたところ、n.sienaさんから「ユースケースに合わせたデータベースアクセス方法を提供するというのは、トランザクションスクリプト的な発想ですね」との言葉をいただいた。

やはり、集約として部署を設定したのであれば、部署リポジトリのsave()メソッドの内部で複数のテーブルにアクセスするのが自然であるという話になった。実装するならば、以下のようなコードになるようだ*3

public class DepartmentRepositoryImpl implements DepartmentRepository{
  @Autowired
  DepartmentDao departmentDao;
  @Autowired
  AffiliationDao affiliationDao;
  @Override
  public void save(Department aDepartment){
    // 部署テーブル側の更新
    departmentDao.update(mapper.map(aDepartment, DepartmentEntity.class));
    // 所属テーブル側の更新(挿入)。この辺はORM依存になりそう
    List<AffiliationEntity> affliations = someMethod(aDepartment);
    for (AffiliationEntity e : affliations){
      affiliationDao.updateOrInsert(e);
    }
  }
}

ここまでの話で、「アクセス先のテーブルが別々でもひとつのリポジトリでまとめて処理するのでいいのだ」と納得したので、その場は別の話題に移った。そのまま遅くまでリレーショナルモデルについてなどの話をして、すっきりした気分で帰宅した。

もやもやの正体:集約への無理解

翌朝、ブログを書こうと改めて整理を試みてみると、集約の境界とは異なる単位でデータベースアクセス手段を提供したくなっていたのは、集約の設計に違和感があるということの現れだということに気づいた。

そもそも、ドメイン駆動設計における集約とはトランザクション整合性の境界であり、なんらかの不変条件に服するデータ群を整合性のある状態に保つためのパターンなのである。

整合性の境界の論理的な意味は、「その内部にあるあらゆるものは、どんな操作をするにかかわらず、特定の不変条件のルールに従う」ということだ。この境界の外部にある、あらゆるものの整合性は、集約とは無関係になる。つまり、集約はトランザクション整合性の境界と同義である。*4

したがって、リポジトリは集約全体をまとめて永続化する以外の手段を提供すべきではない。ORMのDaoが提供するような個別のテーブルに対する更新は、集約の一部を独立して更新する手段を提供するものであり、バグを生みかねないのだ。

 DAOやそれに関連するパターンを使って、集約の一部とみなされるようなデータに対する、きめ細やかなCRUD操作も行えるので、これはドメインモデルと組み合わせて使うのを避けたほうがいいパターンだといえる。通常の条件の下では、集約自身にビジネスロジックなどの内部処理を管理させて、それ以外にはもらさないようにしておきたい。*5

集約の単位を考え直す

今回の例であれば、名前と部員を同時に変更するというのを、トランザクション整合性の求められる操作として見るべきである、つまり「ひとつの部署の名前と部員との間に担保すべき不変条件がある」のであれば、上に示したような部署リポジトリの設計・実装になるであろう。

しかし、もし部署の名前と部員との間に担保すべき不変条件がないのであれば、集約というトランザクション整合性の単位に括るのではなく、別々の集約として整理して、結果整合性*6を用いればよいのだろう。

いずれにせよ、「ドメイン駆動設計だからリポジトリで関連テーブルも併せて更新する」というようなものではなくて、モデル自体を吟味することが必要なのだ。

おわりに

『エリック・エヴァンスのドメイン駆動設計』や、実践している人のブログなどを読んで、何となくわかってきている気がしていたが、実際に適用してみようとすると全く理解できていないことを痛感させられる。まだまだこれからだ。

※なお、この記事で挙げた部署と社員の例は、実際の業務とは関係のない架空のものである。

Special Thanks

n.sienaさん、kabukawaさん、長時間相談に乗っていただき、ありがとうございました。

また、今回の相談の現場となったのは、「OCHaCafe#5 - 避けては通れない!認証・認可」の懇親会でした。勉強会本体もとても勉強になりました。ありがとうございました。

*1:「エンティティ」、「リポジトリ」、「集約」等の用語はドメイン駆動設計におけるものとする。

*2:ERモデルにおける「実体=エンティティ」であり、ドメイン駆動設計の「エンティティ」ではない。

*3:なお、ここではORMで2種類のDaoを使ってそれぞれのテーブルにアクセスしている風で記述しているが、特定のORMの正確な構文にはなっていない。

*4:『実践ドメイン駆動設計』340頁)

*5:『実践ドメイン駆動設計』425頁。

*6:『実践ドメイン駆動設計』350頁

Eclipse(STS)でJavadocコメント挿入時に自動で入力されるユーザ名を変更する(Windows/Mac)

Eclipse(STS)で開発しているとき、Javadocコメントをショートカットキーなどで挿入することがよくある。このときに、@authorタグの項目が自動で入力されるのだが、デフォルトでは値としてOSへのログインユーザ名が使われる*1。これを自由に変更する方法を書く。

f:id:ky_yk_d:20190404225431p:plain

設定方法:eclipse.ini(SpringToolSuite4.ini)を修正する

設定方法は簡単で、eclipse.ini(SpringToolSuite4.ini)*2に以下のように書き込めばいい。

-product
org.springframework.boot.ide.branding.sts4
--launcher.defaultAction
openFile
-startup
../Eclipse/plugins/org.eclipse.equinox.launcher_1.5.200.v20180922-1751.jar
--launcher.library
../Eclipse/plugins/org.eclipse.equinox.launcher.cocoa.macosx.x86_64_1.1.900.v20180922-1751
-vmargs
; ↓これを追加
-Duser.name=other_name
; ↑これを追加
-Dosgi.requiredJavaVersion=1.8
--add-modules=ALL-SYSTEM
-Xms40m
-XstartOnFirstThread
-Dorg.eclipse.swt.internal.carbon.smallFonts
-Xdock:icon=../Resources/sts4.icns
-Xmx1200m

書き込んでから保存し、Eclipse(STS)を一度閉じて改めて起動すれば、設定の変更が反映される。

補足:Macの場合のiniファイルの所在

Windowsであれば、eclipse.ini(SpringToolSuite4.ini)が実行ファイルと同じフォルダにあるのでわかりやすいのだが、Macの場合はEclipse.app(SpringToolSuite4.app)しか見えていないので一瞬どこにあるかわからない。

正解としては、以下のパスにiniファイルは存在する。

Finderを使ってアクセスするには、Eclipse_2018-09.app(SpringToolSuite4.app)を右クリックして、「パッケージの内容を表示」を選択すればいい。これにより、選択したアプリケーションを起動するのではなく、内側に入ることができる、

f:id:ky_yk_d:20190404225619p:plain

パッケージの内容が見えたら、Contents->Eclipse*3と分け入っていく。すると、お目当のiniファイルが見つかる。設定の内容はWindowsであってもMacであっても変わらない。

f:id:ky_yk_d:20190404225808p:plain

参考

iniファイルの設定方法は以下の記事から。

necoyama3.hatenablog.com

Macの場合のiniファイルの所在は以下の質問から。

stackoverflow.com

*1:パッケージ新規作成時にpackage-info.javaを同時に生成した場合も同じ。

*2:余談)iniファイルのシンタックスハイライトは"dosini"とすればよい。 ";"がコメント記号になる。

*3:これはSTSであっても同じ。