Hot Heart, Cool Mind.

会計×IT の深層へ

コード設計の話

今日は地味な話をしたい。といってもいつも地味な話だが。コードや区分の設計は良いシステムを設計するうえでとても大切である。設計にあたっては業務要件をみたすためにどんなコードや区分が必要かという視点が最初にくるのはもちろんだが、それ以外にも考慮すべきことがある。変なコード設計のもとでプログラムを書くと条件判定が複雑化し、読みにくく変更しにくいシステムができあがる。  ここで書くことは、経験のある設計者はたぶん(私のように)先輩から教わるとか自分で体得しているのもので目新しくはないが、まとめて整理してある本も意外にないようである(私が知らないだけかもしれない)。

無意コード化

無意コード化とはコードの各桁に意味を持たせないことである。これは、わりに良く知られている原則だ。「コードの各桁に意味を持たせる」とは、たとえば品番の頭1桁が"9"ならサービスパーツと判断するといった処理をプログラムのなかに記述するケースを指す。コード体系というものは時と共に破たんしていく傾向がある。プログラムがこういうふうになっていると、コード体系の破たんに歩調をあわせてプログラムも破たんしてくる。「品番の頭1桁が"9"ならサービスパーツである」という「知識」はデータベース上に明示的に存在せず、プログラムのあちこちにハードコードされているので、この「知識」を変更しようとすると(たとえば、"9"だけでは足りなくなって"Z"もサービスパーツに割り当てるようになると)、プログラムのどこを直せばよいのか、途方にくれることになる。こうした状況を避けるのが、無意コード化の意図である。

「意味を持たせない」というのは、あくまでプログラムからみての話であり、ユーザの運用上に限って意味づけしたコードを用いることは別にかまわない。ただし意味づけするとコードは長くなるので、ほどほどにした方がよい。

ちょっと難しいのが「ロールアップコード」と「階層化コード」の扱いだ。ロールアップコードは、各桁が順に、大分類・中分類・小分類...を意味するようになっているコードのことで、管理会計では良く使われる。商品分類に用いるケースが代表的だ。階層化コードとは、親子関係のあるコードを組み合わせてひとつの概念を表すもので、勘定科目についてよく用いられる。"1000" が「現金預金」で、"1000-02"が「現金預金(当座預金)」といったふうにコードを設計するケースだ。ロールアップコードも階層化コードも集計階層を表現するという点では共通だが、前者はそれをひとつのコードの各桁で表現し、後者は複数のコードを階層的に組み合わせることで表現する、という違いがある。
ロールアップコードでは、コードの各桁に(集計単位という)意味がある。プログラムでもそれを前提に集計処理をおこなう。この点では無意コードではないようにみえるが、各桁の値を条件判定に用いることはしない。つまりプログラムは商品分類コードの頭1桁が「大分類」であり、頭2桁をあわせると「中分類」となる、というような知識は持つが、頭1桁が'9'の場合は何を意味しているのか、といった知識は持たない。無意コード化の原則の狙いは後者の知識がプログラムに入り込むのを避けることにあるので、ロールアップコードは許容してよいと思う。階層化コードも同様である。ただし、この二種類のコードを用いた場合、別の意味で難しい問題がともなう。階層が変更された場合にどうするのか、という問題だ。特に過去データの取り扱いが問題なのだが、これについての話は別の機会にゆずりたい。

ユーザコードとシステムコードの峻別

メンテナンス責任がユーザ側にあるか開発側にあるかに着目して、コードをユーザコードとシステムコードに分ける。たとえば勘定科目コードはふつうユーザが登録・管理する。そのばあい勘定科目コードは「ユーザコード」である。いっぽう勘定科目の属性として「勘定区分」を持つことにして、その値の範囲を「資産」「負債」「資本」「収益」「費用」と決めたとする。ユーザが値の範囲を変更したいと考えた場合、たとえば「費用」を「営業費用」と「営業外費用」に分けたいと考えた場合は開発側に依頼しなければならない。開発側ではプログラム修正など、必要な措置をとる。これが「システムコード」である。

ユーザコードは、プログラムの処理ロジック中の条件判定に用いるべきではない。たとえば一般会計(G/L)システムでは期末にすべてのP/L勘定の金額を集計ないし差し引きして利益を計算し、その金額を「繰越利益」とよぶB/S勘定に振り替える。このケースにおいて「繰越利益」の勘定科目コードをプログラムに埋め込むべきではない、ということだ。繰越利益勘定であることを示す属性を勘定科目マスタに設けるか、あるいは別のマスタテーブルに繰越利益の勘定科目コードを登録するようにする。
もっともこれは程度にもよる。繰越利益勘定が変更される可能性がまずないとすれば、登録プログラムを開発するのはもったいない。ソースコード中に定数として登録しておいても問題はないかもしれない。そうであっても、その判断にいたる前には、そのコードはユーザコードなのかシステムコードか、まず、きっちり考えるべきだ。ユーザコードであれば、上述したように定数化するにしても、定数定義ファイルの管理方法をよく検討しておく必要がある。

複数の意味を持つコードの分解

システムコードのなかにはシステム処理上の意味を複数もつものがある。たとえば、上述した「勘定区分」は、勘定科目がフローかバランスかという区別と、借方勘定か貸方勘定かという区別を含んでいる。

「勘定区分」に関する意味分解表
〔複合意味コード〕 〔単純意味コード〕
勘定区分 バランスフロー区分 貸借勘定区分
資産 バランス 借方
負債 バランス 貸方
資本 バランス 貸方
収益 フロー 貸方
費用 フロー 借方

複数の意味をもつコードを「複合意味コード」、単一の意味を持つコードを「単純意味コード」と呼んでおく。
たとえば勘定残高テーブルに保持する残高金額は勘定区分をとわず借方を正として保持されており、表示上は勘定区分におうじて符号を変換するとすれば、表示金額の計算ロジックは以下のようになる。

1.複合意味コードを用いた場合

	IF 勘定区分 =「資産」OR「費用」 THEN
		表示金額 = 残高金額
	ELSE
		表示金額 = 残高金額 × -1
	ENDIF

2.単純意味コードを用いた場合

	IF 貸借勘定区分 =「借方」 THEN
		表示金額 = 残高金額
	ELSE
		表示金額 = 残高金額 × -1
	ENDIF

前に述べたように「費用」が「営業費用」と「営業外費用」に細分化された場合、複合意味コードを用いたロジックは修正が必要になる。こうしたロジックはシステムのあちこちに散らばる傾向があるので修正はひと仕事である。修正もれも発生するだろう。単純意味コードを用いていれば修正は不要である。つまり条件判断に単純意味コードを用いるようにすることでシステムの堅牢性が高まる。それに単純意味コードを使用したロジックの方が、「本当にしたいことは何なのか」をよく表していると思う。
こうした理由から、複合意味コードは単純意味コードに分解してから条件判断に用いることを検討すべきである。

さらに、ここでの話を「ユーザコードとシステムコードの峻別」と組み合わせてみよう。先にあげた勘定科目の例をもとにコードの関連を図示すると以下のようになる。
コード設計_コード概念関連図

ここでみられる「ユーザ:複合意味」→「システム:複合意味」→「システム:単純意味」という対応のパターンは、じっさいの設計でよくあらわれる。「システム:複合意味」コードについては本当に必要か、疑ってみるとよい。このケースでは、バランスフロー区分と貸借勘定区分を勘定科目の属性とし勘定区分は廃止する、ということが可能か考えてみるべきだ。バランスフロー区分と貸借勘定区分の組み合わせは4通りしかないのに勘定区分は5通りある。具体的にいうと「負債」と「資本」の区別はバランスフロー区分と貸借勘定区分では捕捉できない。「負債」と「資本」の区別が本当に必要なら勘定区分を残す(か、別の対策をこうじる)ことになる。しかし、単に昔からの習慣で区別しているだけなら勘定区分の廃止は可能だ。そうすれば、「ユーザ:複合意味」→「システム:単純意味」(右上→左下)という、より単純な対応パターンに還元できる。そうすることによって抽象化の層が減りユーザの認識とシステムのじっさいの処理ロジックが一致する。しかし、別の理由でそうはできないケースもある。たとえば、消費税区分を「システム:複合意味」コードとした場合、売上仕入区分・内税外税区分・税率区分・本体税額区分などいくつかの「システム:単純意味」コードに還元することができるだろう。しかしそんなにたくさんのコードを伝票に入力するのはだれでも嫌に違いない。このへんのバランス感覚もコード設計には必要だ。