Quantcast
Channel: とあるコンサルタントのつぶやき
Viewing all 64 articles
Browse latest View live

Part 2. Entity Framework Core 1.0 の基本的な使い方

$
0
0

では引き続き、Entity Framework Core の利用方法を解説していきます。

■ 基本的な Entity Framework Core の利用方法

従来の Entity Framework と異なり、EF Core では O/R マッパーファイル(*.dbml)を使うことができません。このため、O/R マッピング(データベーステーブルのどこを構造体クラスのどこにマッピングするのか?)はすべてコードで指定する必要があります。ツールを利用して自動生成させることも(ある程度は)可能ですが、現時点(2016/07/02)では、手で書いてしまったほうがやりやすいと思います。

まずは以下 2 つのファイルを用意します。

  • データベースファイル
    • プロジェクトファイル直下に App_Data フォルダを掘り、pubs.mdf ファイルを置きます。
    • データベースファイルは公開する必要がないため、wwwroot 下に置く必要はありません。私は昔の名残で App_Data という名前を使っていますが、この名前である必要性も特にありません。お好みで変えていただいて結構です。
  • モデルファイル
    • プロジェクトファイル直下に Models フォルダを切り、Pubs.cs ファイルを作成します。

image_thumb[13]

Pubs.cs ファイルに、データを取り出すための構造体クラスを記述し、そこに O/R マッピング情報を記述していきます。今回は、全テーブルをやるとキリがないので、以下 6 つのテーブルについてだけ取り出してマッピングしてみます。

  • authors : 著者データ
  • titles : 書籍データ
  • titleauthor : 著者と書籍の多対多中間テーブル
  • publishers : 出版社データ
  • stores : 店舗データ
  • sales : 売上データ

image

最終的なコードは以下の通りです(※ OnConfiguring() メソッド内のパス情報は適宜書き換えてください)。えらい長いコードで恐縮ですが;、順番に説明していきます。


using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;

namespace Decode2016.WebApp.Models
{
    public partial class PubsEntities : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder options)
        {
            options.UseSqlServer(
                @"data source=(LocalDB)\mssqllocaldb;attachdbfilename=|DataDirectory|\pubs.mdf;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"
                .Replace("|DataDirectory|", @"C:\Users\nakama\Documents\Visual Studio 2015\Projects\Decode2016.WebApp\src\Decode2016.WebApp\App_Data"));
        }

        public DbSet<Author> Authors { get; set; }
        public DbSet<Title> Titles { get; set; }
        public DbSet<Publisher> Publishers { get; set; }
        public DbSet<Store> Stores { get; set; }
        public DbSet<Sale> Sales { get; set; }
        public DbSet<TitleAuthor> TitleAuthors { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Sale>().HasKey(s => new { s.StoreId, s.OrderNumber, s.TitleId });
            modelBuilder.Entity<TitleAuthor>().HasKey(ta => new { ta.AuthorId, ta.TitleId });
            modelBuilder.Entity<Sale>().HasOne(s => s.Title).WithMany(t => t.Sales).IsRequired();
            modelBuilder.Entity<Sale>().HasOne(s => s.Store).WithMany(s => s.Sales).IsRequired();
            modelBuilder.Entity<Publisher>().HasMany(p => p.Titles).WithOne(t => t.Publisher).IsRequired();
            modelBuilder.Entity<Author>().HasMany(a => a.TitleAuthors).WithOne(ta => ta.Author).IsRequired();
            modelBuilder.Entity<Title>().HasMany(t => t.TitleAuthors).WithOne(ta => ta.Title).IsRequired();
        }
    }

    [Table("authors")]
    public partial class Author
    {
        [Column("au_id"), Required, MaxLength(11), Key]
        public string AuthorId { get; set; }

        [Column("au_fname"), Required, MaxLength(20)]
        public string AuthorFirstName { get; set; }

        [Column("au_lname"), Required, MaxLength(40)]
        public string AuthorLastName { get; set; }

        [Column("phone"), Required, MaxLength(12)]
        public string Phone { get; set; }

        [Column("address"), MaxLength(40)]
        public string Address { get; set; }

        [Column("city"), MaxLength(20)]
        public string City { get; set; }

        [Column("state"), MaxLength(2)]
        public string State { get; set; }

        [Column("zip"), MaxLength(5)]
        public string Zip { get; set; }

        [Column("contract"), Required]
        public bool Contract { get; set; }

        [Column("rowversion"), Timestamp, ConcurrencyCheck]
        public byte[] RowVersion { get; set; }

        public ICollection<TitleAuthor> TitleAuthors { get; set; }

    }

    [Table("publishers")]
    public partial class Publisher
    {
        [Column("pub_id"), Required, MaxLength(4)]
        public string PublisherId { get; set; }

        [Column("pub_name"), MaxLength(40)]
        public string PublisherName { get; set; }

        [Column("city"), MaxLength(20)]
        public string City { get; set; }

        [Column("state"), MaxLength(2)]
        public string State { get; set; }

        [Column("country"), MaxLength(30)]
        public string Country { get; set; }

        public ICollection<Title> Titles { get; set; }
    }

    [Table("titles")]
    public partial class Title
    {
        [Column("title_id"), Required, MaxLength(6), Key]
        public string TitleId { get; set; }

        [Column("title"), Required, MaxLength(80)]
        public string TitleName { get; set; }

        [Column("type"), Required, MaxLength(12)]
        public string Type { get; set; }

        [Column("price")]
        public decimal? Price { get; set; }

        [Column("advance")]
        public decimal? Advance { get; set; }

        [Column("royalty")]
        public int? Royalty { get; set; }

        [Column("ytd_sales")]
        public int? YeatToDateSales { get; set; }

        [Column("notes"), MaxLength(200)]
        public string Notes { get; set; }

        [Column("pubdate"), Required]
        public DateTime PublishedDate { get; set; }

        [Column("pub_id"), MaxLength(4)]
        public string PublisherId { get; set; }

        public Publisher Publisher { get; set; }

        public ICollection<Sale> Sales { get; set; }

        public ICollection<TitleAuthor> TitleAuthors { get; set; }
    }

    [Table("sales")]
    public partial class Sale
    {
        // ※ 複合キーは Data Annotation で指定できないため、Fluent API を使う

        [Column("stor_id"), Required, MaxLength(4)]
        public string StoreId { get; set; }

        [Column("ord_num"), Required, MaxLength(20)]
        public string OrderNumber { get; set; }

        [Column("ord_date"), Required]
        public DateTime OrderDate { get; set; }

        [Column("qty"), Required]
        public int Quantity { get; set; }

        [Column("payterms"), Required, MaxLength(12)]
        public string PayTerms { get; set; }

        [Column("title_id"), Required, MaxLength(6)]
        public string TitleId { get; set; }

        public Store Store { get; set; }
        public Title Title { get; set; }
    }

    [Table("stores")]
    public partial class Store
    {
        [Column("stor_id"), Required, MaxLength(4), Key]
        public string StoreId { get; set; }

        [Column("stor_name"), Required, MaxLength(40)]
        public string StoreName { get; set; }

        [Column("stor_addr"), Required, MaxLength(40)]
        public string Address { get; set; }

        [Column("city"), Required, MaxLength(20)]
        public string City { get; set; }

        [Column("state"), Required, MaxLength(22)]
        public string State { get; set; }

        [Column("zip"), Required, MaxLength(5)]
        public string Zip { get; set; }

        public ICollection<Sale> Sales { get; set; }
    }

    [Table("titleauthor")]
    public partial class TitleAuthor
    {
        [Column("au_id"), Required]
        public string AuthorId { get; set; }

        [Column("title_id"), Required]
        public string TitleId { get; set; }

        [Column("au_ord")]
        public byte AuthorOrder { get; set; }

        [Column("royaltyper")]
        public int RoyaltyPercentage { get; set; }

        public Author Author { get; set; }

        public Title Title { get; set; }

    }
}

■ コードの構造について

上記のソースの各クラスの役割は以下の通りです。

  • PubsEntites
    • データベース接続を管理するクラス。DbContext クラスから派生させて作成する。
    • この接続下で取り扱うテーブル一覧もここに記述される。
    • 接続文字列の指定方法は複数あるが、基本的には OnConfiguring メソッド内で指定する。
      • EF Core では SQL Server 以外にも接続できるため、SQL Server に接続したい場合には、project.json ファイルで Microsoft.EntityFrameworkCore.SqlServer ライブラリを組み込んだ上で、options.UseSqlServer(…) として接続先を指定する。
  • Author, Publisher, Title, Sale, Store, TitleAuthor クラス
    • データベースの各テーブルに対応させて作成した構造体クラス。
    • データベース上の 1 レコードが、これらのオブジェクトの 1 インスタンスに読みだされる。
      • このため、データベース上のテーブル名は複数形、C# のクラス名は単数形になるのが一般的。
    • 属性(クラス定義やプロパティ定義の上につけられたカギカッコ)により、データベースとのマッピング方法を指定する。
      • ほとんどのマッピングは属性で指定できるが、複合キーやリレーションシップに関する情報は(現時点では)属性では指定できない。このような場合には、PubsEntities クラスの OnModelCreating() メソッド内でコードで O/R マッピング情報を指定する。
      • 属性を使って O/R マッピングを指定する方法をデータアノテーション方式、OnModelCreating() メソッド内でコードを使って O/R マッピングを指定する方法を Fluent API 方式と呼ぶ。

おおまかなコードの構造は以下の通りです。(リレーションシップの指定などに関しては少し重要なので、後述します。)


    public partial class PubsEntities : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder options)
        {
            // ここに接続文字列を書く
        }

        // ここにテーブル一覧を書く
        public DbSet<Author> Authors { get; set; }
        public DbSet<Title> Titles { get; set; }
        public DbSet<Publisher> Publishers { get; set; }
        public DbSet<Store> Stores { get; set; }
        public DbSet<Sale> Sales { get; set; }
        public DbSet<TitleAuthor> TitleAuthors { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // ここにデータアノテーションで指定できない O/R マッピング情報を書く
        }
    }

    [Table("authors")]
    public partial class Author
    {
        [Column("au_id"), Required, MaxLength(11), Key]
        public string AuthorId { get; set; }
        [Column("au_fname"), Required, MaxLength(20)]
        public string AuthorFirstName { get; set; }
        ...
    }

    [Table("publishers")]
    public partial class Publisher
    {
        [Column("pub_id"), Required, MaxLength(4)]
        public string PublisherId { get; set; }
        ...
    }
    ...

■ リレーションシップの O/R マッピング方法について

リレーションシップについては、以下のように実装します。

  • 1 : 多について
    • 例えば、出版社(Publisher)と書籍(Title)は 1 : 多の関係になりますが、これは以下のように実装します。
    • Publisher クラス側
      • 1 つの Publisher に複数の Title が紐づけられるので、ICollection<Title> Titles プロパティを作成します。
    • Title クラス側
      • 1 つの Title には 1 つの Publisher が紐づくので、Publisher Publisher プロパティを作成します。
      • PublisherId プロパティは持っても持たなくても構いませんが、通常はあると便利なので持たせてしまいます。
    • 上記準備を済ませたうえで、リレーションシップに関する O/R マッピング情報を OnModelCreating() メソッドに記述します。

[Table("publishers")]
public partial class Publisher
{
    [Column("pub_id"), Required, MaxLength(4)]
    public string PublisherId { get; set; }
    [Column("pub_name"), MaxLength(40)]
    ...
    public ICollection<Title> Titles { get; set; }
}

[Table("titles")]
public partial class Title
{
    [Column("title_id"), Required, MaxLength(6), Key]
    public string TitleId { get; set; }
    [Column("title"), Required, MaxLength(80)]
    public string TitleName { get; set; }
    ...
    [Column("pub_id"), MaxLength(4)]
    public string PublisherId { get; set; }
    public Publisher Publisher { get; set; }
}

modelBuilder.Entity<Publisher>().HasMany(p => p.Titles).WithOne(t => t.Publisher).IsRequired();
  • 多 : 多について
    • 例えば、著者(Author)と書籍(Title)は、多 : 多テーブルである TitleAuthor テーブルを介して 多 : 多 の関係を持ちます。
    • このような多 : 多の関係については、代表的には以下の 2 つの方式での O/R マッピングが考えられます。
      • ① 直接、多 : 多の関係をオブジェクトで表現する
        • Author オブジェクトのプロパティとして ICollection<Title> Titles を、Title オブジェクトのプロパティとして ICollection<Author> Authors を持つが…
        • 中間テーブルである TitleAuthor に対応するオブジェクトは作らない
      • ② 中間テーブルまで含めてオブジェクトで表現する
        • 中間テーブルである TitleAuthor に対応するオブジェクトを明示的に作り、Author オブジェクトのプロパティとして ICollection<TitleAuthor> TitleAuthors を、Title オブジェクトのプロパティとして ICollection<TitleAuthor> TitleAuthors を持たせる
        • すなわち、2 組の 1 : 多 の関係であるとして表現してしまう
    • どちらにもメリット・デメリットがありますが、一般的には②方式の方が幅広く利用できます。
      • 今回のサンプルのように、著者(Author)と書籍(Title)の間の TitleAuthor に印税料(Royalty)などの属性がついている場合には、明確にオブジェクトとして作成する必要があります。
    • ①の方式の場合には、単に 1 : 多の関係を 2 つ作成するだけになります。

■ LINQ クエリの記述

O/R マッピングファイルができたら、あとは LINQ クエリを実行します。今回は簡単のため、Startup.cs の app.Run() 内を書き換えて実行してみましょう。


app.Run(async (context) =>
{
    using (PubsEntities pubs = new PubsEntities())
    {
        var query = from a in pubs.Authors where a.State == "CA" select a;
        await context.Response.WriteAsync(query.Count().ToString());
    }
});

正しく動作すれば、15 件という結果が帰ってくるはずです。

image

なお、EF で必要となる LINQ クエリの記述方法についてはここでは説明しませんが、LINQ クエリは EF を扱う上での必須技術であるため、まだ知らないという方は必ず学習してください。拙著「LINQ テクノロジ入門」は EF の前進となる LINQ to SQL という技術を使って書かれていますが、LINQ クエリの書き方そのものは基本的に変わりません。こちらの本をざっと流し読みしていただければ、LINQ の基本的な考え方などは学習できると思います。

■ 従来の EF からの大きな変更点について

(ここはちょっと難しいのでわかる人だけ読んでください) Entity Framework Core では様々な変更が入っていますが、実用側面から見た場合、以下の 2 つは非常に大きな変更点ですので、ここで解説しておきます。

Lazy Loading の廃止

従来の EF では、リレーションシップの先にあるデータを、クエリ実行後に後から手繰れるというトンデモ仕様が含まれていました。現場側の人間からすると、誰だこの学術的機能を入れた人は;、と全力でツッコミたかったわけですが、EF Core ではこの仕様が廃止されました。このため、以下のクエリは実行時に例外が発生します。

image

image

もちろん、場合によっては「クエリ実行時に、リレーションシップの先までデータを取得しておいてほしい」ということもあるはずです。この場合には、クエリ発行前に、明示的に Include, ThenInclude 命令で取り込む対象を指定してください。

image

非同期処理

従来の EF では、ToList() や FirstOrDefault() などによるクエリ実行は同期的にしか実行できませんでしたが、こうしたクエリ実行命令に、非同期処理版が追加されました。このため、以下のようなクエリは await/async 構文を利用して、以下のように記述できるようになりました。


var query = from a in pubs.Authors where a.State == "CA" select a;
var result = query.ToList();
↓
var result = await query.ToListAsync();

EF のこのような機能拡張に合わせて、ASP.NET MVC のアクションメソッドや Web API でも、async 構文を使った定義ができるようになりました。


[HttpGet]
public ActionResult ShowTitlesByPublishers()
{
    using (PubsEntities pubs = new PubsEntities())
    {
        var query = pubs.Publishers....;
        ViewData["TitlesByPublisher"] = await query.ToList();
    }
    return View();
}

↓

[HttpGet]
public async Task<ActionResult> ShowTitlesByPublishers()
{
    using (PubsEntities pubs = new PubsEntities())
    {
        var query = pubs.Publishers....;
        ViewData["TitlesByPublisher"] = await query.ToListAsync();
    }
    return View();
}

この新機能については様々なところでよく紹介されていますが、クライアント側(UI 処理)における async/await とは意味合いがずいぶん違うので注意してください。ざっくり説明すると、以下の通りです。

  • クライアント側における async/await 処理
    • UI スレッド(メインスレッド)上で時間がかかる処理をしてしまうと、「UI が固まる」という現象が起こる。
    • この問題を起こさないように、長時間(だいたい 30 msec よりも長い時間)を要する処理を別スレッドに切り出して行うために、async/await を使う。
  • サーバ側における async/await 処理
    • サーバ側は、通常、下図のようなマルチスレッド処理で複数のユーザの処理を捌いている。
    • この際、データアクセスのように時間のかかる処理をしてしまうと、当該スレッドは待機状態となり、CPU が遊んでしまう。
    • async/await 処理を行って、CPU を明示的に他のスレッドに回すことで、より CPU の利用効率を高めることができる。

image

サーバサイドではもともとマルチスレッドで処理が行われているため、「サーバが固まる」という現象が起こるわけではないですし、仮に async/await を明示的に行わなかったとしても、OS のマルチスレッド制御機能により、自動的に CPU リソースが他のスレッドに回されますので、すぐさま問題が起こるというわけではありません。ただ、ASP.NET ランタイムが Windows OS 以外でも動作するという話になってくると、OS によってはこのマルチスレッド制御の機能が貧弱なケースも考えられ、そのような場合には「明示的なリソース解放」を行わないと性能が出ない、というケースが出てくるかもしれません。正直、今どきの OS であればそうそう問題が起こるケースはないだろうとは思いますが、とはいえお作法としては、async/await 処理をきちんと書いた方が、環境依存のトラブルが出にくくなるという意味では安心です。

いずれにしても、サーバ側の async/await は、クライアント側の async/await とは利用目的が違う、という点は知っておくとよいでしょう。

■ 接続文字列の管理方法について

先の例では、接続文字列をハードコーディングしましたが、実際のアプリではいくつか課題があります。解決策をいくつかここで示しておきます。

  • アプリが配置されているディレクトリの自動解決
    • 従来の ASP.NET で利用していた |DataDirectory| 文字列は残念ながら ASP.NET Core では利用できません。Startup.cs ファイル内であれば、ソースコードのフォルダ名を比較的簡単に解決できます。これを用いて、Startup.cs から PubsEntities.cs にデータを引き渡すとよいでしょう。
    • 具体的には以下の通りです。
      • Startup.cs にコンストラクタを作る。コンストラクタの引数に IHostingEnvironment を付けておくと、自動的にホスティング環境の情報を渡してくれます。(詳細はここでは解説しませんが、コンストラクタインジェクションと呼ばれる ASP.NET ランタイムの DI コンテナ機能によるものです。)
      • さらに、Pubs.cs ファイル側でこれを拾うようにコードを修正します。
    • ※ (つぶやき)本来を言えば PubsEntities 側でパスを自動解決するように実装したいのですが、1.0.0 RTM 版の時点では PlatformServices クラスに ApplicationEnvironment プロパティしかなく、HostingEnvironment プロパティが存在しないため、そのような実装ができません。将来的には PlatformServices クラスから解決できるようになるのではないかと思います。

public class Startup
{
    public static string App_Data { get; set; }

    public Startup(IHostingEnvironment env)
    {
        App_Data = Path.Combine(env.ContentRootPath, "App_Data");
    }
...

public partial class PubsEntities : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder options)
    {
        options.UseSqlServer(
            @"data source=(LocalDB)\mssqllocaldb;attachdbfilename=|DataDirectory|\pubs.mdf;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"
            .Replace("|DataDirectory|", Startup.App_Data));
    }
...
  • 開発環境/運用環境の切り替え
    • #if DEBUG 文を差し込んでおき、開発環境と運用環境の設定を切り替えます。

#if DEBUG
            options.UseSqlServer(
                @"data source=(LocalDB)\mssqllocaldb;attachdbfilename=|DataDirectory|\pubs.mdf;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"
                .Replace("|DataDirectory|", Startup.App_Data));
#else
            options.UseSqlServer(
                @"Server=tcp:xxxxxxxx.database.windows.net,1433;Database=pubs;User ID=xxxxxxxx@xxxxxxxx;Password=xxxxxxxx;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;");
#endif

なお、ASP.NET Core では構成設定値を web.config ファイルに書きません。この点は従来の ASP.NET Web Forms から大きく変わっている点ですのでご注意ください。背景は以下の通りです。

  • 変更になった最大の理由は、従来の ASP.NET Web Forms では web.config の設定が肥大化し、メンテナンスがつらくなってしまったため
  • 従来の web.config ファイルには、大別して以下の 2 つの設定情報が書かれたが、ASP.NET Core では、これらの情報を分離して扱う。
    • ① ASP.NET ランタイムパイプラインのカスタマイズ方法や設定
      • Startup.cs ファイルの ConfigureServices(), Configure() の 2 つのメソッド内にコードとして記述する。
      • ConfigureServices() で DI コンテナの設定を、Configure() でパイプラインの設定をする。
    • ② 接続文字列などの純粋な設定値
      • 単純なものや、セキュリティ上の問題がないものであれば、上記の例のようにソースコード中にハードコードしてしまうのがラク。
      • どうしてもファイルに切り出したい場合などは、ASP.NET Core の新しい構成設定システムを利用する。(ASP.NET Core の新しい構成設定システムについてはこのエントリでは解説しないので、こちらを参考にしてください。)

■ その他の制約事項・注意事項について

その他、現在の Entity Framework Core 1.0 に関する注意点は以下の通りです。

  • Azure の SQL Database に対する利用について
    • Windows Azure の PaaS データベースである SQL Database に対してクエリを実行する場合、スロットリング(流量制限)によりクエリ実行が失敗するケースがあります。このため、SQL Database に対してクエリを実行する場合には、自動リトライ処理を入れるのがベストプラクティスになっています。
    • EF6 ではこの自動リトライ処理の組み込みが簡単に行えましたが、EF Core 1.0 ではまだこれが実装されていません(2016/07/02 時点)。近いうちに実装されるでしょうが、現時点では制約事項と考えておく必要があります。(ちょっと自力で作り込むのは大変;)
  • 自動トランザクションとの組み合わせについて
    • ASP.NET Core では、System.Transactions 配下の TransactionScope が利用できません。Windows プラットフォームを前提とできる場合には、ランタイムとして Full CLR を利用することで TransactionScope の利用もできなくはないですが、その場合でもあまり組み合わせて利用することはオススメはしません。
    • これはそもそも Enity Framework と自動トランザクションは、設計として相性が悪いためです。
      • Entity Framework では、基本的に、データベースの入出力とは、エンティティというデータの塊の出し入れである、と発想を持っており、「エンティティ」という単位を超えるトランザクション制御はほとんどないよね? という思想を持っています。もちろん実際の業務を見るとエンティティという単位を超えるトランザクション制御が必要になる場合も存在するのですが、そういうものはごく一部だし、高速性を要求する処理だったりすることが多いので、ストプロで実装しちゃってね、という割り切り思想を持っています。(よいか悪いかはともかく)
      • 一方、自動トランザクションは、そうした割り切り型の思想はなく、むしろ「開発者の都合次第でいかようにも組んでよい」という、Entity Framework よりも低水準の技術です。このため、自動トランザクションと Entity Framework を組み合わせようとすると、「UPDLOCK ロックヒントが簡単につけられない」などの問題に突き当たることになります。
    • このため、EF Core を使う場合、(少なくとも現時点では)以下のようにするのがオススメです。(ロックヒントなどの問題は、中長期的には解決されてくる問題かもしれませんが、現時点では以下のように考えておくとよいと思います。)
      • 自動トランザクションとは組み合わせないこと。
      • どうしてもトランザクション制御が必要になるところは、ストプロで実装する。
      • あまりにもストプロ実装が増えそうなら、EF Core ではなく、生の System.Data.SqlClient などを利用することを検討してみる。

以上が EF Core 1.0 の基本的な使い方になります。その他、より詳しい情報については、de:code 2016 DEV-003 セッション 「新しく生まれ変わったデータアクセステクノロジ~Entity Framework Core 1.0 の全貌~」などを見ていただくとよいと思います。


Part 3. ASP.NET MVC Core 1.0 の基本的な使い方

$
0
0

では、データアクセスができるようになったところで、ASP.NET MVC Core 1.0 の基本的な使い方を見ていくことにします。

■ ASP.NET MVC の超簡単解説

ASP.NET Web Forms しか開発したことがない方のために、超ざっくりと ASP.NET MVC を解説しておきます。(ASP.NET MVC を触ったことがある方はここは飛ばしていただいて OK です。)

  • ASP.NET MVC とは、論理 3 階層型の Web アプリケーション(UI/BC/DAC に分解される Web アプリケーション)を開発する際に、UI 部分を「きれいに分解しながら」作ることができる技術のひとつ。
  • クライアントのブラウザから、特定のコントローラクラスのアクションメソッドを呼び出すと、それに対応する処理が動き、対応するビューによりレスポンス HTML データが作成され、ブラウザに返却される、という仕組みになっている。

image

ASP.NET MVC に関して、まず特に覚えておいていただきたいポイントは以下の 3 つです。

  • ブラウザからのリクエストは、URL で指定されたコントローラクラスのアクションメソッドで処理される。
    • 例えば、ブラウザから http://localhost:xxx/Sample01/GetAuthors/ という URL を呼び出すと、Sample01Controller.cs というクラスの、GetAuthors() というメソッドが呼び出される。(第一引数でコントローラクラス名を指定し、第二引数でアクションメソッド名を指定する。
    • このメソッドの中では、ビジネスロジッククラスやデータアクセスクラスを呼び出してデータベースからデータを取り出したり、処理結果をビューに引き渡したりする。
  • クライアントに送り返す HTML データは、対応するビューファイル(.cshtml ファイル)により作成される。
    • コントローラクラスからデータを受け取り、それを使って、クライアントに送り返す HTML ページを作成する。
  • ASP.NET MVC では、モデルに相当するものが何であるのかは決まっていない。
    • ここは重要なポイントですが、”MVC” = Model + View + Controller という名前がついているものの、ASP.NET MVC において明確に扱いが決まっているのは Controller と View のみであり、Model が何であるのかの定義はありません
    • 一般的には、以下のうちのいずれか(または複数)が Model に相当します。(詳細は本 blog を読み進めていくうちにわかってくると思いますので、ここではわからなくてもよいです。)
      • ① データベースとやり取りされるデータの構造(=EF のデータモデル)
      • ② ビューに対して引き渡すデータの構造(=ビューモデル)
      • ③ クライアントから送られてくるデータの構造(=リクエストパラメータバインディングのモデル)

では前置きはこのぐらいにして、具体的な実装を見ながら説明していきます。

■ ASP.NET MVC Core ランタイムの組み込み

ASP.NET Core 1.0 のデフォルト状態では、何もアプリケーションランタイムが組み込まれていません。このため、Startup.cs の Configure() メソッドを修正し、ASP.NET MVC Core 1.0 ランタイムを組み込みます。


public class Startup
{
    ...

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole();
        if (env.IsDevelopment()) app.UseDeveloperExceptionPage();

        app.UseStaticFiles();
        app.UseMvcWithDefaultRoute();
    }
}

Part 1. で説明したように、project.json ファイルにてライブラリに対する参照設定を加えたのち、ConfigureServices() メソッドに services.AddMvc() メソッドを、また Configure() メソッドに app.UseMvcWithDefaultRoute() メソッドを追加します。これにより、このアプリで MVC が利用できるようになります。

なお、ConfigureServices(), Configure() メソッドは、サンプルコードによって引数が違っていたりします。これは ASP.NET ランタイムの DI コンテナ機能によるものですが、わからなければとりあえず引数が違っていてもあまり気にしなくてよい、と思っておいてください。

ではまず手始めに、データベースから著者データを取ってきて一覧表示するアプリを作ってみます。(いつもながら芸がないですがw)

■ コントローラクラスとビューファイルの配置

まずプロジェクトに Controllers という名前のフォルダを掘り、そこに HomeController.cs という名前で C# のクラスファイルをひとつ追加します。なお、以降ではフォルダ名やクラス名、ファイル名などの大文字・小文字、単数形・複数形に注意してください。ASP.NET MVC Core では、名前が正しく規約に基づいてつけられていることを元に動作します。標準的な名付けルールに則ることで、構成設定などを書く手間を減らすことができます。(この考え方を「設定より規約」と呼び、ASP.NET MVC はこの考え方に基づいて作られています。)

image

作成したら、以下のような HomeController クラスを実装します。ポイントは以下の通りですが、いずれも ASP.NET MVC のお約束の書き方だと思って覚えてください。

  • コントローラクラスは、必ず Controller クラスを継承させて作る。
  • アクションメソッドの戻り値は必ず ActionResult クラスとする。
  • 実際にメソッドを終了するときは、View() を返す。(※ このメソッドは、Controller クラスが持っているメソッドで、ActionResult クラスを継承した ViewResult クラスのインスタンスを返します。)
  • このページを呼び出す HTTP プロトコルを制限したい場合には、属性 [HttpGet] や [HttpPost] などを指定します。(無指定だとどんなプロトコルでも呼び出しが可能になります。)

using Microsoft.AspNetCore.Mvc;

namespace Decode2016.WebApp.Controllers
{
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            return View();
        }
    }
}

続いて、Views というフォルダ、その下に Home というフォルダを作成し、その下に Index.cshtml という名前で MVC ビューページという項目を配置します。

image

この中に、クライアントに送り返す HTML データを記述します。いったんファイルの中身を全部消し、以下のように記述します。


<!DOCTYPE html>
<html>
<head>
    <title>著者データ一覧</title>
</head>
<body>
    これからコードを書く...
</body>
</html>

ここまで作成したらアプリを実行し、http://localhost:xxxx/Home/Index/ を呼び出します。すると、① コントローラクラスのアクションメソッドが呼び出され、② そこから return View(); によりビューファイルが呼び出され、③ Views/Home/Index.cshtml ファイルから HTML データが作られ、ブラウザに送り返されます。

image

※ なお、この例では http://localhost:xxxx/ を呼び出しも同じ結果になります。これは、ASP.NET MVC では、パスを省略すると /Home/Index を呼び出したと解釈されるためです。(Startup.cs ファイルに記述した app.UseMvcWithDefaultRoute(); の中に、既定値の設定が含まれています)

では、基本的な構造ができたので、ここにプログラムを作りこんでいきます。

■ データアクセスページの作成

HomeController クラスの Index() メソッド内に、データを取り出すコードを記述します。ビジネスロジッククラスを別に作って呼び出しても構いませんが、今回のサンプルではここに直接データアクセスコードを記述してしまうことにします。

取り出したデータは、ViewData という汎用的な入れ物を使ってビューに引き渡すことができます。


using Microsoft.AspNetCore.Mvc;
using Decode2016.WebApp.Models;
using System.Linq;

namespace Decode2016.WebApp.Controllers
{
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            using (PubsEntities pubs = new PubsEntities())
            {
                var query = from a in pubs.Authors select a;
                ViewData["Authors"] = query.ToList();
            }
            return View();
        }
    }
}

続いて、Index.cshtml ファイルを書き換えます。*.cshtml ファイルでは Razor と呼ばれる構文を利用し、サーバ側でレンダリングを行います。


@using Decode2016.WebApp.Models

<!DOCTYPE html>
<html>
<head>
    <title>著者データ一覧</title>
</head>
<body>
    @{
        var authors = (List<Author>)ViewData["Authors"];
    }
    <table>
        <thead>
            <tr>
                <th>著者ID</th>
                <th>著者名</th>
                <th>電話番号</th>
                <th>契約有無</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var a in authors)
            {
                <tr>
                    <td>@a.AuthorId</td>
                    <td>@a.AuthorFirstName @a.AuthorLastName</td>
                    <td>@a.Phone</td>
                    <td>@(a.Contract ? "契約あり" : "契約なし")</td>
                </tr>
            }
        </tbody>
    </table>
</body>
</html>

要点を以下に解説します。

  • *.cshtml ファイルは、サーバサイドで HTML データを作り出すために使われます。
    • classic ASP(ASP.NET の前の技術)に非常によく似た技術です。大昔を知っている方ならむしろ馴染み深いものかもしれません。
  • Razor 構文では、@ マークを使って C# のコードを記述します。
    • 基本構文は @{ … } ですが、カッコなしで @foreach { … } といきなり命令を書くこともできます。

また、Razor 構文では HTML 出力を簡単に作れるようになっています。例えば…

以下のように書くべきところを… 以下のように書ける
@{
    Write(DateTime.Now);
}
@DateTime.Now
@{
    Write(“<td>” + a.AuthorId + “</td>”);
}
<td>@a.AuthorId</td>

といった感じです。慣れるとサクサクと書けます。実行してみるとこんな感じです。

image

このサンプルを見て、<table> タグを自力で書かなければならないのかよ! と思われた方、はい、その通りです

  • ASP.NET Web Forms の場合、GridView という便利な部品が存在していましたが、ASP.NET MVC Core では(今のところ)そのような高機能な便利部品は存在していません。ちなみに ASP.NET MVC 5 には WebGrid という部品は存在しており、これについてはいずれ ASP.NET MVC Core にも移植されると思いますが、ASP.NET Web Forms の GridView に比べるとかなり機能的には劣ります。なので、過度な期待は禁物です。
  • ASP.NET Web Forms で利用していたような高機能なグリッド部品が必要な場合には、3rd party 製の部品に頼る形になります。この詳細については、この後の SPA 型アプリ開発のセクションで解説します。

■ ASP.NET MVC のフォルダ/ファイルのレイアウトルール

ここまで見てきた例からわかるように、ASP.NET MVC では、フォルダ/ファイル/メソッドのレイアウトと命名にルールがあります。上では 1 つのページしかありませんでしたが、もしこれが本格的な Web アプリになったらどうなるかを考えてみます。例えば、以下のような業務構造・画面遷移を持つアプリを作ろうと思ったとします。

image

この場合には、以下のようにファイルをレイアウトします。(※ コントローラクラスの中には、各ページに対応する複数のアクションメソッドが並ぶことになります。) このファイルのレイアウト関係を、しっかり頭に入れておいてください。

image

基本的な ASP.NET MVC のアプリの作り方がわかったら、今度はページの構造化をしていくことにします。

Part 4. ページレイアウトの構造化

$
0
0

ここまでで ASP.NET MVC Core を使った簡単なページはできましたが、ちょっとダサいです;。なのでこれを jQuery や Bootstrap でちょっと綺麗にしてみます。

■ Bootstrap の利用

Bootstrap は非常に有名な CSS フレームワークのひとつで、これを利用することにより、レスポンシブ対応の Web サイトを比較的容易に構築していくことができます。Bootstrap については日本語でも多数の書籍が出ていますので、詳細についてはここでは書きません。先に作成した Index.cshtml ファイルの Web ページを、Bootstrap を使ってカッコよくしてみると、以下のようになります。

image

Index.cshtml ファイルのソースコードは以下の通りです。(かなり長いですが、大半は呪文です。要点はソースコードの後ろに書いてあります。)


@using Decode2016.WebApp.Models

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>de:code 2016</title>
    <meta name="viewport" content="width=device-width, intial-scale=1.0" />

    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>

    <style type="text/css">
        @@media only screen and (min-width : 768px) {
            html {
                position: relative;
                height: 100%;
            }
            body {
                height: 100%;
                padding-top: 50px;
                padding-bottom: 50px;
            }
            .contentWrapper {
                overflow: auto;
                height: 100%;
            }
            .contentBody {
                padding-top: 10px;
                padding-bottom: 10px;
                min-height: 100%;
            }
            .footer {
                position: fixed;
                bottom: 0;
                margin-bottom: 0;
                width: 100%;
                height: 50px;
                background-color: #f5f5f5;
            }
            .navbar-custom-responsive {
                position: fixed;
                width:100%;
                top:0px;
                border-width: 0 0 1px 0;
            }
        }
        @@media only screen and (min-width : 0px) {
            html {
            }
            body {
            }
            .contentWrapper {
            }
            .contentBody {
                padding-top: 10px;
                padding-bottom: 10px;
            }
            .footer {
                bottom: 0;
                margin-bottom: 0;
                width: 100%;
                height: 50px;
                background-color: #f5f5f5;
            }
            .navbar-custom-responsive {
                margin-bottom: 0;
            }
        }
    </style>

    <style type="text/css">
        div.row ul {
            padding-left: 30px;
        }
    </style>


</head>
<body>
    <nav class="navbar navbar-static-top navbar-default navbar-custom-responsive" role="navigation">
        <div class="container">
            <div class="navbar-header">
                <a class="navbar-brand" href="/"><span class="glyphicon glyphicon-leaf"></span>&nbsp;&nbsp;de:code 2016</a>
                <button type="button" class="navbar-toggle" data-target="#navbar" data-toggle="collapse">
                    <span class="sr-only">ナビゲーションの表示</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
            </div>
            <div class="navbar-collapse collapse" id="navbar">
                <ul class="nav navbar-nav navbar-right">
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">
                            各種サンプル&nbsp;<span class="caret"></span>
                        </a>
                        <ul class="dropdown-menu">
                            <li><a href="/Sample01/FilterByStateWithSort">従来型の Web アプリ</a></li>
                            <li><a href="/Sample02/FilterByStateWithSort">SPA 型の Web アプリ</a></li>
                            <li><a href="/Sample03/FilterByState">jQuery + Bootstrap + knockout.js</a></li>
                            <li><a href="/Sample04/ListAuthors">非 SPA ベースでの実装</a></li>
                            <li><a href="/Sample05/ListAuthors">SPA ベースでの実装</a></li>
                        </ul>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

    <div class="contentWrapper">
        <div class="container contentBody">

            @{
                var authors = (List<Author>)ViewData["Authors"];
            }
            <table class="table table-condensed table-striped table-hover">
                <thead>
                    <tr>
                        <th>著者ID</th>
                        <th>著者名</th>
                        <th>電話番号</th>
                        <th>契約有無</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach (var a in authors)
                    {
                        <tr>
                            <td>@a.AuthorId</td>
                            <td>@a.AuthorFirstName @a.AuthorLastName</td>
                            <td>@a.Phone</td>
                            <td>@(a.Contract ? "契約あり" : "契約なし")</td>
                        </tr>
                    }
                </tbody>
            </table>

        </div>
    </div>

    <footer class="footer">
        <div class="text-right" style="padding-top: 13px; padding-right: 20px; ">&copy; 2016 Microsoft Corporation. All rights reserved.</div>
    </footer>
</body>
</html>

ややこしいソースコードになっていますが、要点は以下の通りです。細かいコードはわからなくてもとりあえず構いません

  • Bootstrap は簡単のため、CDN 上で公開されているものをそのまま参照して利用しています。
  • ぐだぐた書かれているスタイルシート <style type=”text/css”> 部分は、ヘッダー・フッター部の制御のためのものです。
  • <body> 内は、ヘッダー部分+中身のコンテンツ部分+フッター部分の 3 ブロックで構成されています。
    • ヘッダー部分 <nav> は、Bootstrap の機能を使って作っています。これにより、ドロップダウンタイプのショートカットメニューを実現しています。(リンク先はとりあえずダミーですが)
    • <div class=”container contentBody”> 内が本体部分です。Part 3. の最後で作成したテーブルをここに移植してあります。(Bootstrap のデザインを適用するため、<table>タグに class=”table table-condensed table-striped table-hover” の指定をつけています。これによりきれいなデザインになります。)
    • フッター部分 <footer> は自力で作成しています。フッターの位置制御は、スタイルシートで行っています。

■ ページの構造化

さて、今回はまだ 1 つのページしか作っていませんが、実際の Web サイトでは複数の Web ページを作成するのがふつうで、しかもこれは下図のように共通のヘッダーやフッター、あるいはライブラリを使うのがふつうです。

image

上に示したように、個々のページは下図のようなレイアウトを持ちますが、共通部分を各ページの *.cshtml ファイルにコピペで貼り付けていたら、明らかにサイトのメンテナンス性は落ちます。

image

このため、実際の Web サイトでは、下図のように構造化を行い、① 共通 UI 部分などについてはレイアウトページに掃き出す、② 個々のページは差分だけ実装する、③ ページごとに組み込むか否かは異なるものの、よく使うものについては部品として用意しておく、ということを行います。

image

具体的には以下の作業を行います。

  • /Views/Shared フォルダを作成し、ここに _Layout.cshtml ファイルを作成する。
  • /Views/Home/Index.cshtml ファイルを、差分情報のみに変更する。

[/Views/Shread/_Layout.cshtml ファイル]

ポイントとなるのは以下の 5 箇所です。コードからわかるように、required:false になっているものは、指定してもしなくても OK になっています。

  • @ViewData[“Title”] : ページタイトルを個別ページ側から指定できるようにするためのもの
  • @RenderSection(“Libraries”, required: false) : 標準的に組み込まれている、Bootstrap, jQuery 以外のライブラリを個々のページで組み込む際に利用
  • @RenderSection(“Styles”, required: false) : 個々のページで追加の CSS 指定をしたい場合に利用
  • @RenderBody() : 本体部分を埋め込み
  • @RenderSection(“Scripts”, required: false) : 個々のページの JavaScript を指定

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>@ViewData["Title"]</title>
    <meta name="viewport" content="width=device-width, intial-scale=1.0" />

    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>

    @RenderSection("Libraries", required: false)

    <style type="text/css">
        @@media only screen and (min-width : 768px) {
            html {
                position: relative;
                height: 100%;
            }

            body {
                height: 100%;
                padding-top: 50px;
                padding-bottom: 50px;
            }

            .contentWrapper {
                overflow: auto;
                height: 100%;
            }

            .contentBody {
                padding-top: 10px;
                padding-bottom: 10px;
                min-height: 100%;
            }

            .footer {
                position: fixed;
                bottom: 0;
                margin-bottom: 0;
                width: 100%;
                height: 50px;
                background-color: #f5f5f5;
            }

            .navbar-custom-responsive {
                position: fixed;
                width: 100%;
                top: 0px;
                border-width: 0 0 1px 0;
            }
        }

        @@media only screen and (min-width : 0px) {
            html {
            }

            body {
            }

            .contentWrapper {
            }

            .contentBody {
                padding-top: 10px;
                padding-bottom: 10px;
            }

            .footer {
                bottom: 0;
                margin-bottom: 0;
                width: 100%;
                height: 50px;
                background-color: #f5f5f5;
            }

            .navbar-custom-responsive {
                margin-bottom: 0;
            }
        }
    </style>

    @RenderSection("Styles", required: false)

</head>
<body>
    <nav class="navbar navbar-static-top navbar-default navbar-custom-responsive" role="navigation">
        <div class="container">
            <div class="navbar-header">
                <a class="navbar-brand" href="/"><span class="glyphicon glyphicon-leaf"></span>&nbsp;&nbsp;de:code 2016</a>
                <button type="button" class="navbar-toggle" data-target="#navbar" data-toggle="collapse">
                    <span class="sr-only">ナビゲーションの表示</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
            </div>
            <div class="navbar-collapse collapse" id="navbar">
                <ul class="nav navbar-nav navbar-right">
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">
                            各種サンプル&nbsp;<span class="caret"></span>
                        </a>
                        <ul class="dropdown-menu">
                            <li><a href="/Sample01/FilterByStateWithSort">従来型の Web アプリ</a></li>
                            <li><a href="/Sample02/FilterByStateWithSort">SPA 型の Web アプリ</a></li>
                            <li><a href="/Sample03/FilterByState">jQuery + Bootstrap + knockout.js</a></li>
                            <li><a href="/Sample04/ListAuthors">非 SPA ベースでの実装</a></li>
                            <li><a href="/Sample05/ListAuthors">SPA ベースでの実装</a></li>
                        </ul>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

    <div class="contentWrapper">
        <div class="container contentBody">

            @RenderBody()

        </div>
    </div>

    <footer class="footer">
        <div class="text-right" style="padding-top: 13px; padding-right: 20px; ">&copy; 2016 Microsoft Corporation. All rights reserved.</div>
    </footer>

    @RenderSection("Scripts", required: false)

</body>
</html>

[/Views/Home/Index.cshtml ファイル]

こちらは差分情報だけ残せばよくなるので、以下のコードのみで済むようになります。


@using Decode2016.WebApp.Models
@{
    Layout = "_Layout";
    ViewData["Title"] = "著者データ一覧";
}

@{
    var authors = (List<Author>)ViewData["Authors"];
}
<table class="table table-condensed table-striped table-hover">
    <thead>
        <tr>
            <th>著者ID</th>
            <th>著者名</th>
            <th>電話番号</th>
            <th>契約有無</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var a in authors)
        {
            <tr>
                <td>@a.AuthorId</td>
                <td>@a.AuthorFirstName @a.AuthorLastName</td>
                <td>@a.Phone</td>
                <td>@(a.Contract ? "契約あり" : "契約なし")</td>
            </tr>
        }
    </tbody>
</table>

上記のように修正して実行しても、全く動作結果が変わらないことを確認していただければと思います。

■ 共通レイアウトの強制

上記のサンプルで、Layout=”_Layout”; というコードがありますが、これはレイアウトページを指定するためのコードです。このコードをいちいち個別に指定していると面倒、ということもあると思います。このような場合には、/Views/_ViewStart.cshtml ファイルを追加し、そこに以下のように記述してください。これで個々のページでのレイアウトページ指定は不要になります。


@{
    Layout = "_Layout";
}

image

以上で ASP.NET MVC Core の Web アプリの骨格ができあがりました。引き続き、ASP.NET MVC Core を使った SPA 型 Web アプリ開発の基礎を説明していきます。

Part 5. ASP.NET Web API を使った SPA 型 Web アプリ開発

$
0
0

では引き続き、ASP.NET Web API を使って SPA 型で同じアプリを開発してみたいと思います。

■ SPA 型 Web アプリとは?

SPA (Single Page Application)型 Web アプリとは、単一ページで機能を提供する Web アプリです。具体的な設計・実装モデルとしては、データを Web API で取得し、画面を構築する処理をブラウザ側にもってくる形になります。

image

非 SPA 型 Web アプリと SPA 型 Web アプリの作り方の違いについては、de:code 2016 DEV-010 セッションにて詳しく解説しているのでそちらを見ていただくことにして、ここでは具体的な作り方について解説したいと思います。ここでは、① すべての著者データを一覧表示する、② 州によるフィルタリングを行う、という 2 つの画面を作成してみます。

image image

■ ファイルの配置

まず、コントローラクラス Sample01Controller.cs とビューファイル ShowAllAuthors.cshtml, ShowAuthorsByState.cshtml を配置します。作成するのは 2 つのページですが、コントローラクラスは業務(ビューのフォルダ)単位でよいので、ここでは 1 つだけ作ります。(コントローラとビューは、先にビューから考えると配置しやすいです。)

image

まずは空のページを作っておきましょう。Sample01Controller.cs ファイルにアクションメソッド 2 つを用意しておきます。


using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Decode2016.WebApp.Controllers
{
    public class Sample01Controller : Controller
    {
        [HttpGet]
        public ActionResult ShowAllAuthors()
        {
            return View();
        }

        [HttpGet]
        public ActionResult ShowAuthorsByState()
        {
            return View();
        }
    }
}

*.cshtml ファイルの方はこれから作っていきますので、とりあえず中身はタイトルぐらいで OK です。


@{ ViewData["Title"] = "全著者データの一覧"; }

<h4>全著者データの一覧</h4>

@{ ViewData["Title"] = "州による著者データの検索"; }

<h4>州による著者データの検索</h4>

作成したら、http://localhost:xxx/Sample01/ShowAllAuthors/ や http://localhost:xxx/Sample01/ShowAuthorsByState/ などを呼び出していただき、ページが表示されることを確認します。

image

■ ASP.NET Web API の作成

次に、ブラウザからデータを取り出すために必要な ASP.NET Web API の作成を行います。ASP.NET MVC Core ランタイムには ASP.NET Web API ランタイムも包含されていますので、このまま Web API を開発していくことが可能です。まずは全件のデータを取り出すための GetAllAuthors() メソッドを、Sample01Controller.cs ファイルに追記します。


using Decode2016.WebApp.Models;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Decode2016.WebApp.Controllers
{
    public class Sample01Controller : Controller
    {
        [HttpGet]
        public ActionResult ShowAllAuthors()
        {
            return View();
        }

        [HttpGet]
        public ActionResult ShowAuthorsByState()
        {
            return View();
        }

        [HttpGet]
        public List<Author> GetAllAuthors()
        {
            using (PubsEntities pubs = new PubsEntities())
            {
                var query = from a in pubs.Authors select a;
                return query.ToList();
            }
        }
    }
}

MVC と Web API のアクションメソッドはよく似ていますが、戻り値が異なる点に注意してください。実装できたら、ブラウザから http://localhost:xxxx/Sample01/GetAllAuthors を呼び出すと、この Web API の動作を確認することができます。

image

ここで押さえてほしいポイントは以下の通りです。

  • MVC と Web API は、同一のコントローラクラス上に混在させることができる。(もちろんコントローラクラスを分けても構いません)
  • サーバからは、JSON と呼ばれる形式でデータが送り返される。
  • JSON データの各フィールドの先頭一文字が、小文字に自動的に置換されている

3 点目に関しては若干注意が必要です。一般的に、サーバ側の C# ではクラス名やフィールド名は先頭を大文字にしますが、ブラウザ側の JavaScript では先頭を小文字にすることが多いです。このため、ASP.NET Web API の背後で利用されている Newtonsoft の Json.NET では、自動的にこの大文字/小文字変換を行うようになっています(※ 正確には ASP.NET Core 1.0 版から変更されています)。この挙動は、カジュアルな開発では便利ですが、きっちり開発する業務アプリ開発では気持ち悪いと感じる人もいると思います(← 私的には気持ち悪い;)。この挙動を抑えるためには、Startup.cs ファイルに以下のコードを追加してください。この blog では、下記コードがあるものとして解説を進めます。


public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc()
            .AddJsonOptions(options =>
            {
                // 大文字・小文字の自動補正機能を無効化
                options.SerializerSettings.ContractResolver = null; // CamelCasePropertyNamesContractResolver が刺さっているためこれを外す
            });

    //// 以下は XML フォーマッタを入れたり、単一文字列を返す Web API を作りたい場合に入れるとよい設定。
    //services.Configure<MvcOptions>(options =>
    //{
    //    options.RespectBrowserAcceptHeader = true;
    //    options.OutputFormatters.RemoveType<Microsoft.AspNetCore.Mvc.Formatters.StringOutputFormatter>();
    //});
}

これを加えて同じ Web API を呼び出すと、以下のようになります。各フィールドの先頭一文字が C# と同じく大文字になります(=そのまま送出されている)。

image

さて、このまま使ってもよいのですが、実際の一覧表はすべてのフィールドのデータが必要なわけではないので、一部だけ絞り込むことにします。Models フォルダの下に AuthorOverview.cs クラスを追加し、以下のような構造体クラスを実装します。

image


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Decode2016.WebApp.Models
{
    public class AuthorOverview
    {
        public string AuthorId { get; set; }
        public string AuthorName { get; set; }
        public string Phone { get; set; }
        public string State { get; set; }
        public bool Contract { get; set; }
    }
}

そして、先に実装した Sample01Controller.cs クラスの GetAllAuthors() メソッドを以下のように修正します。


[HttpGet]
public List<AuthorOverview> GetAllAuthors()
{
    using (PubsEntities pubs = new PubsEntities())
    {
        var query = from a in pubs.Authors
                    select new AuthorOverview()
                    {
                        AuthorId = a.AuthorId,
                        AuthorName = a.AuthorFirstName + " " + a.AuthorLastName,
                        Phone = a.Phone,
                        State = a.State,
                        Contract = a.Contract
                    };
        return query.ToList();
    }
}

このように実装すると、列が絞り込まれた JSON データが返されるようになります。(ブラウザから直接叩いて動作確認してみるとよいでしょう)

■ ブラウザ側の実装

引き続き、ブラウザ側の処理を実装します。ブラウザ側では、① Web API からのデータの取得、② 取得したデータの一覧表示、の 2 つが必要です。前者には jQuery を使うのがよいですが、後者については様々な方法があります。考え方や選択方法については de:code のセッションで解説しているのでそちらを確認していただくことにして、ここでは最も簡単(=予備知識が不要)な方法として、knockout.js を使ったデータバインドを使ってみたいと思います。

/Views/Sample01/ShowAllAuthors.cshtml ファイルを修正し、まずは jQuery を使ってすべてのデータを Web API から取り寄せる処理を記述します。下図にあるように、JavaScript のコードはページ固有の JavaScript コードブロックに記述しますので、@section Scripts { … } に記述します。

image_thumb16


@{ ViewData["Title"] = "全著者データの一覧"; }

<h4>全著者データの一覧</h4>

@section Scripts {
    <script type="text/javascript">
        $(function () {
            $.getJSON("/Sample01/GetAllAuthors", function (result) {
                console.debug(result);
            });
        });
    </script>
}

Ctrl + F5 キーでアプリを実行してから http://localhost:xxx/Sample01/ShowAllAuthors ページを呼び出しますが、その際、F12 ツールを利用すると、リモート通信の中身や、console.debug() 命令でロギングした情報を確認することができます。これにより、Web サーバと正しく通信できているのかが確認できます。

image

続いて、取り寄せたデータを knockout.js ライブラリを利用して表示してみます。まず、knockout.js ライブラリの組み込みは @section Libraries { … } で行いますが、このライブラリは複数のページで利用する可能性のあるライブラリです。このため、/Views/Sample01/ShowAllAuthors.cshtml ファイルにハードコードしてしまうとメンテナンス性が落ちます。このような場合には、/Views/Shared フォルダ下にファイルを作成しておき、これを組み込むようにしておくとよいでしょう。

image

[/Views/Shared/_ImportsLibraryKnockout.cshtml](中身はたったの一行しかありませんが、敢えて切り出しておき、複数のページで再利用する)


<script src="https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js"></script>

[/Views/Sample01/ShowAllAuthors.cshtml]


@{ ViewData["Title"] = "全著者データの一覧"; }

@section Libraries {
    @Html.Partial("_ImportsLibraryKnockout")
}

<h4>全著者データの一覧</h4>

@section Scripts {
    <script type="text/javascript">
        $(function () {
            $.getJSON("/Sample01/GetAllAuthors", function (result) {
                console.debug(result);
            });
        });
    </script>
}

ここまでできたら、knockout.js を使ってデータバインドを行います。基本的な考え方は下図の通りで、ko.observableArray() を利用して ViewModel を作成し、これを介してコードと UI との間でデータバインドを行います。(knockout.js を本格的に使いたい、というのでなければ、細かいコードは理解しなくて構いません。ざっくり「こんな感じ」と理解してもらえれば十分です。)

image

image

ソースコードは以下のようになります。


@{ ViewData["Title"] = "全著者データの一覧"; }

@section Libraries {
    @Html.Partial("_ImportsLibraryKnockout")
}

<h4>全著者データの一覧</h4>

<div class="table-responsive">
    <table class="table table-condensed table-striped table-hover">
        <thead>
            <tr>
                <th>著者ID</th>
                <th>著者名</th>
                <th>電話番号</th>
                <th>州</th>
                <th>契約有無</th>
            </tr>
        </thead>
        <tbody data-bind="foreach: Authors">
            <tr>
                <td data-bind="text: AuthorId"></td>
                <td data-bind="text: AuthorName"></td>
                <td data-bind="text: Phone"></td>
                <td data-bind="text: State"></td>
                <td>
                    <input type="checkbox" disabled data-bind="checked: Contract" />&nbsp;
                    <text data-bind="text: (Contract ? '契約あり' : '契約なし')"></text>
                </td>
            </tr>
        </tbody>
    </table>
</div>

@section Scripts {
    <script type="text/javascript">
        $(function () {
            var viewModel = {
                Authors: ko.observableArray()
            };
            ko.applyBindings(viewModel);

            $.getJSON("/Sample01/GetAllAuthors", function (result) {
                viewModel.Authors(result);
            });
        });
    </script>
}

実行結果は下図のようになります。

image

では、同じ要領で、州によるフィルタリングアプリを開発してみたいと思います。

■ 州によるフィルタリングアプリの実装

州によるフィルタリングアプリを作成するためには、① 州の一覧データを取り出す Web API と、② 指定された州に属する著者の一覧を検索取得する Web API、の 2 つが必要です。Sample01Controller.cs クラスに以下の 2 つのメソッドを追加しましょう。


[HttpGet]
public string[] GetStates()
{
    using (PubsEntities pubs = new PubsEntities())
    {
        var query = pubs.Authors.Select(a => a.State).Distinct();
        return query.ToArray();
    }
}

[HttpGet]
public List<AuthorOverview> GetAuthorsByState(string state)
{
    if (Regex.IsMatch(state, "^[A-Z]{2}$") == false) throw new ArgumentOutOfRangeException("state");

    List<AuthorOverview> result;
    using (PubsEntities pubs = new PubsEntities())
    {
        var query = pubs.Authors.Where(a => a.State == state)
                    .Select(a => new AuthorOverview()
                    {
                        AuthorId = a.AuthorId,
                        AuthorName = a.AuthorFirstName + " " + a.AuthorLastName,
                        Phone = a.Phone,
                        State = a.State,
                        Contract = a.Contract
                    });
        result = query.ToList();
    }
    return result;
}

なお、GetAuthorsByState() メソッドについては、[HttpGet] で定義する方法と、[HttpPost] で定義する方法の両方が考えられます。正直なところ、どっちでも動作するのでどっちでもいいっちゃいいのですが;、このケースでは [HttpGet] にしておいたほうが便利です。理由は以下の通り。

  • [HttpGet] にしておくと、ブラウザから簡単に動作確認ができます。
    • http://localhost:xxx/Sample01/GetAuthorsByState/?state=CA などとして呼び出しを行うと、HTTP-GET でこの Web API を呼び出して、動作確認をとることができて便利です。
  • このケースでは、HTTP プロトコルの規約からすると、[HttpGet] のほうが適切です。
    • 通常、同じ州に対しては同じ著者一覧が帰ってくるはずです。このような場合には [HttpGet] のほうがよいです。
    • 一方で、「処理要求伝票データを送って、処理結果伝票データを受け取る」ような設計の場合には、処理結果が毎回変わる可能性があるため、[HttpPost](伝票を投入する)の方がベターです。

ただし注意点として、HTTP-GET プロトコルで Web API を呼び出す場合、jQuery ライブラリはブラウザ側で呼び出し結果を自動的にキャッシュします。これは HTTP プロトコルの規約の考え方からすると正しいのですが、その一方で、業務アプリだと HTTP-GET の場合でもキャッシュしてほしくない、という場合もあると思います。このような場合には、jQuery の $.ajaxSetup() 処理を使って、キャッシュを無効化してください。集約例外処理も含め、_Layout.cshtml ファイルに以下のようなコードを追加するとよいでしょう。


<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>@ViewData["Title"]</title>
    <meta name="viewport" content="width=device-width, intial-scale=1.0" />

    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>

    <script type="text/javascript">
    $(function () {
        $.ajaxSetup({
            cache: false,
            error: function (xhr, status, err) { alert("通信エラーが発生しました。"); } // 集約通信例外ハンドラ
        });

        window.onerror = function (message, url, lineNumber) {
            console.log(message);
            var msg = "処理中にエラーが発生しました。" + message;
            alert(msg);
            return true;
        };
    });
    </script>

    @RenderSection("Libraries", required: false)

(以下略...)

続いて、UI を実装します。/Views/Sample01/ShowAuthorsByState.cshtml ファイルを開き、以下を実装します。考え方は上と同じです。


@{ ViewData["Title"] = "州による著者データの検索"; }

@section Libraries {
    @Html.Partial("_ImportsLibraryKnockout")
}

<h4>州による著者データの検索</h4>

<div>
    <select id="ddlStates" data-bind="options: States"></select>
    <button id="btnShowAuthors">データ表示</button>
</div>

<hr />

<div class="table-responsive">
    <table id="tblAuthors" class="table table-condensed table-striped table-hover">
        <thead>
            <tr>
                <th>著者ID</th>
                <th>著者名</th>
                <th>電話番号</th>
                <th>州</th>
                <th>契約有無</th>
            </tr>
        </thead>
        <tbody data-bind="foreach: Authors">
            <tr>
                <td data-bind="text: AuthorId"></td>
                <td data-bind="text: AuthorName"></td>
                <td data-bind="text: Phone"></td>
                <td data-bind="text: State"></td>
                <td>
                    <input type="checkbox" disabled data-bind="checked: Contract" />&nbsp;
                    <text data-bind="text: (Contract ? '契約あり' : '契約なし')"></text>
                </td>
            </tr>
        </tbody>
    </table>
</div>

@section Scripts {
    <script type="text/javascript">
        $(function () {
            $("#tblAuthors").hide(); // css('display', 'none') と同じ

            // 後から値を入れたい場合には、ko.observable() と ko.observableArray() を割り当てておく
            var viewModel = {
                States: ko.observableArray(),
                Authors: ko.observableArray()
            };
            ko.applyBindings(viewModel);

            // サーバから州一覧を取り寄せてバインド
            $.getJSON("/Sample01/GetStates", function (result) {
                viewModel.States(result);
            });

            $("#btnShowAuthors").click(function () {
                // クエリ文字列を引数に渡すには、第二パラメータにオブジェクトを渡す
                $.getJSON("/Sample01/GetAuthorsByState", { state: $("#ddlStates").val() }, function (result) {
                    viewModel.Authors(result); // データを observableArray に流し込み
                    $("#tblAuthors").show(); // css('display', 'block') と同じ
                });
            });
        });
    </script>
}

できあがったら http://localhost:xxxx/Sample01/ShowAuthorsByState/ を呼び出して動作を確認してください。SPA 型で作られたデータバインド Web アプリケーションが動作します。

image

■ 様々な SPA 型アプリケーションの作り方

ここでは、ASP.NET Web API + ASP.NET MVC + jQuery + Bootstrap + knockout.js というライブラリの組み合わせにより SPA 型 Web アプリケーションを開発しました。しかし、ここまでのコードからわかるように、この方法は <table> タグを手で組み上げていく方法であるため、実装効率は必ずしもよくありません。業務アプリケーションのように、表が大量に出てくるようなケースでは、この方法では生産性がどうしても上がらないでしょう。このような場合には、ライセンス料は発生するものの、3rd party 製の高水準 UI ライブラリなどを使ったほうが生産性としてはよくなります。

クライアント側のライブラリをどのように選択するのかは、SPA 型 Web アプリケーション開発における大きな命題です。業務アプリケーションの場合には、下記のように高水準 UI 部品を必要とするか否かによって、基本的な方針を決めていくのがラクだと思いますが、最先端の開発技術を使って高度な UI を作っていくのであれば、まったく別の考え方を採ったほうがよいこともあります。

image

本エントリは ASP.NET Core 1.0 の概要を解説するものであるため、この部分についてはこれ以上踏み込みませんが、特にクライアント側のライブラリ選択や開発指針についてどのように考えていけばよいのかについては、de:code 2016 のセッション、およびそこからさらに発展させていただいている HTML5 experts さんのサイトが参考になると思います。より深い理解を進めたい方は、これらに目を通していくことをオススメします。

Part 6. ASP.NET MVC Coreによるデータ更新アプリ

$
0
0

では引き続き、更新系アプリを作成してみます。Web API でデータ更新アプリを作るのはちょっと骨が折れるので、まずは ASP.NET MVC Core でデータ更新アプリを作ってみることにします。若干遠回りではありますが、こちらをいったん理解しておくと、Web API でのデータ更新アプリの作り方も理解しやすくなるでしょう。

image image

■ ファイルの追加

ここまで作ってきたプロジェクトに /Controllers/Sample02Controller.cs、/Views/Sample02/ListAuthors.cshtml、/Views/Sample02/EditAuthor.cshtml ファイルを追加しておきます。

image

■ 著者一覧ページの実装

まずは Part 3 で解説した方法をもとに、著者一覧ページを作成しましょう。


using Decode2016.WebApp.Models;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Decode2016.WebApp.Controllers
{
    public class Sample02Controller : Controller
    {
        [HttpGet]
        public ActionResult ListAuthors()
        {
            using (PubsEntities pubs = new PubsEntities())
            {
                var query = pubs.Authors
                            .Select(a => new AuthorOverview()
                            {
                                AuthorId = a.AuthorId,
                                AuthorName = a.AuthorFirstName + " " + a.AuthorLastName,
                                Phone = a.Phone,
                                State = a.State,
                                Contract = a.Contract
                            });
                ViewData["Authors"] = query.ToList();
            }
            return View();
        }
    }
}

ListAuthors.cshtml ファイル側では、著者データ編集ページに遷移できるように、著者 ID 部分をハイパーリンク化しておきます。例えば、”172-32-1176″ のデータをクリックした際には、/Sample02/EditAuthor/172-32-1176 に遷移するようにしておきます。このようにしておくと、URL の 3 つ目の値を、EditAuthor() アクションメソッドの id パラメータにより受け取ることができるようになります。(この辺については、ASP.NET MVC の書籍に詳しく解説されていますので、調べてみてください。)


@using Decode2016.WebApp.Models
@{
    ViewBag.Title = "編集対象の著者選択";
}

<h4>編集対象となる著者を選択してください。</h4>

@{
    if (ViewData["Authors"] != null)
    {
        var data = ViewData["Authors"] as List<AuthorOverview>;
        <div class="table-responsive">
            <table class="table table-condensed table-striped table-hover">
                <thead>
                    <tr>
                        <th>著者ID</th>
                        <th>著者名</th>
                        <th>電話番号</th>
                        <th>州</th>
                        <th>契約有無</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach (AuthorOverview a in data)
                    {
                        <tr>
                            <td><a href="/Sample02/EditAuthor/@a.AuthorId">@a.AuthorId</a></td>
                            <td>@a.AuthorName</td>
                            <td>@a.Phone</td>
                            <td>@a.State</td>
                            <td><input type="checkbox" disabled @(a.Contract ? "checked" : "") /></td>
                        </tr>
                    }
                </tbody>
            </table>
        </div>
    }
}

<hr />

<p>
    <a href="/">業務メニューに戻る</a>
</p>

■ データ編集ページの実装

続いてデータ編集ページを実装します。データ編集ページは、① 一覧ページからハイパーリンクで飛んできて画面が表示され(HTTP-GET)、② フォームデータを入力し、再度呼び出す(HTTP-POST)ことになります。EditAuthor ページ側の実装については、HTTP-GET, HTTP-POST のプロトコルで、初回/ポストバックのどちらであるのかを見分けるとよいでしょう。

image

さて、このような更新系アプリケーションの実装の厄介なところは、単体入力チェックを、ブラウザ上/サーバ側の両方で実装しなければならない点です。このポイントについては、以前、2009 年に書いたこちらのエントリの考え方と同じですが、ASP.NET MVC を使う場合、二重実装の回避には、より洗練された手法であるデータアノテーション方式を使います。

image

まず、入力フォームと同じデータ構造を持つ構造体クラス(ViewModel クラスと呼ばれます)を作成し、ここにデータアノテーションを使って、単体入力エラーチェックの内容を指定します。(ViewModel のコードはどこに定義してもよいですが、この Sample02 でしか利用しないので、Sample02Controller.cs クラスの内部クラスとして定義してしまうとよいでしょう。)


public class EditViewModel
{
    public string AuthorId { get; set; }

    [Required(ErrorMessage = "著者名(名)は必須入力項目です。")]
    [RegularExpression(@"^[\u0020-\u007e]{1,20}$", ErrorMessage = "著者名(名)は半角 20 文字以内で指定してください。")]
    public string AuthorFirstName { get; set; }

    [Required(ErrorMessage = "著者名(姓)は必須入力項目です。")]
    [RegularExpression(@"^[\u0020-\u007e]{1,40}$", ErrorMessage = "著者名(姓)は半角 40 文字以内で指定してください。")]
    public string AuthorLastName { get; set; }

    [Required(ErrorMessage = "電話番号は必須入力項目です。")]
    [RegularExpression(@"^\d{3} \d{3}-\d{4}$", ErrorMessage = "電話番号は 012 345-6789 のような形式で指定してください。")]
    public string Phone { get; set; }

    [Required(ErrorMessage = "州は必須入力項目です。")]
    [RegularExpression(@"^[A-Z]{2}$", ErrorMessage = "州は半角大文字 2 文字で指定してください。")]
    public string State { get; set; }
}

続いて、アクションメソッドを定義します。要点は以下の 2 つです。

  • アクションメソッドの引数
    • string id パラメータをつけておくと、URL の第 3 引数を受け取ることができます。
  • ビューへのデータ引き渡し
    • データベースからデータを取ってきて、EditViewModel クラスのインスタンスに代入し、return View() の引数とすることで、View 側にデータを引き渡すことができます。(※ 通常、ビューへは ViewData[…] を使ってデータを引き渡しますが、ViewModel クラスから JavaScript のエラーチェックロジックを動的に生成させたい場合には、後述するように *.cshtml 側の先頭に @model を定義し、データを return View() の引数として渡します。なお、コードからわかるように、ViewData[…] との併用は可能です。)

[HttpGet]
public ActionResult EditAuthor(string id)
{
    // 当該著者 ID のデータを読み取る
    Author editAuthor = null;
    using (PubsEntities pubs = new PubsEntities())
    {
        var query = from a in pubs.Authors
                    where a.AuthorId == id
                    select a;
        editAuthor = query.FirstOrDefault();
    }

    // View に引き渡すデータを準備する
    EditViewModel vm = new EditViewModel()
    {
        AuthorId = editAuthor.AuthorId,
        AuthorFirstName = editAuthor.AuthorFirstName,
        AuthorLastName = editAuthor.AuthorLastName,
        Phone = editAuthor.Phone,
        State = editAuthor.State
    };

    // View にデータを引き渡すにあたり、入力データと周辺データを分けておく。
    // (ViewModel に周辺データを入れることで、ViewModel を完全にフォームモデルに一致させるように設計)
    using (PubsEntities pubs = new PubsEntities())
    {
        var query = pubs.Authors.Select(a => a.State).Distinct();
        ViewData["AllStates"] = query.ToList();
    }

    return View(vm); // 編集画面を作成して返す
}

次に、*.cshtml ファイルを作成します。ASP.NET MVC では、jQuery Validation を内部で利用するため、JavaScript ライブラリを追加で組み込みます。後で再利用できるように、/Views/Shared/_ImportsLibraryValidation.cshtml と /Views/Shared/_ImportsStyleValidation.cshtml に共有ファイルとして切り出しておきましょう。

image

[/Views/Shared/_ImportsLibraryValidation.cshtml]

<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/localization/messages_ja.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.min.js"></script>

[/Views/Shared/_ImportsStyleValidation.cshtml]


<style type="text/css">
    @@media only screen and (min-width : 0px) and (max-width : 767px) {
    }

    @@media only screen and (min-width : 768px) and (max-width : 991px) {
        dl {
            width: 738px; /* 750-12 */
            margin: 6px;
        }

        dl dt {
            float: left;
        }

        dl dd {
            margin-left: 200px;
        }
    }

    @@media only screen and (min-width : 992px) and (max-width : 1199px) {
        dl {
            width: 958px; /* 970-12 */
            margin: 6px;
        }

        dl dt {
            float: left;
        }

        dl dd {
            margin-left: 200px;
        }
    }

    @@media only screen and (min-width : 1200px) {
        dl {
            width: 1158px; /* 1170-12 */
            margin: 6px;
        }

        dl dt {
            float: left;
        }

        dl dd {
            margin-left: 200px;
        }
    }

    /* エラーメッセージ用 */
    /* jQuery unobtrusive validation 用 */
    .field-validation-error {
        color: #ff0000;
    }

    .field-validation-valid {
        display: none;
    }

    .input-validation-error {
        border: 2px solid #ff0000;
        background-color: #ffeeee;
    }

    .validation-summary-errors {
        font-weight: bold;
        color: #ff0000;
    }

    .validation-summary-valid {
        display: none;
    }

    /* jQuery Validation 用 */
    .error {
        color:red
    }
    input.error, select.error, textarea.error {
        border: 2px solid red;
        background-color: #ffeeee;
    }

</style>

続いて /Views/Sample02/EditAuthor.cshtml ファイルを実装します。(要点は後述)


@model Decode2016.WebApp.Controllers.Sample02Controller.EditViewModel
@{
    ViewBag.Title = "著者データの編集";
}
@section Libraries {
    @Html.Partial("_ImportsLibraryValidation")
}

@section Styles {
    @Html.Partial("_ImportsStyleValidation")
}

<h4>著者データを修正してください。</h4>

@using (Html.BeginForm("EditAuthor", "Sample02", new { id = Model.AuthorId }))
{
    <dl>
        <dt>著者ID</dt>
        <dd>@Model.AuthorId</dd>
    </dl>
    <dl>
        <dt>著者名(名)</dt>
        <dd>@Html.TextBoxFor(m => m.AuthorFirstName, new { data_val_specialnamecheck = "指定された名前(名・姓の組み合わせ)は使えません。" }) @Html.ValidationMessageFor(m => m.AuthorFirstName, "*")</dd>
    </dl>
    <dl>
        <dt>著者姓(姓)</dt>
        <dd>@Html.TextBoxFor(m => m.AuthorLastName, new { data_val_specialnamecheck = "指定された名前(名・姓の組み合わせ)は使えません。" }) @Html.ValidationMessageFor(m => m.AuthorLastName, "*")</dd>
    </dl>
    <dl>
        <dt>電話番号</dt>
        <dd>@Html.TextBoxFor(m => m.Phone) @Html.ValidationMessageFor(m => m.Phone, "*")</dd>
    </dl>
    <dl>
        <dt>州</dt>
        <dd>
            @{
                List<string> states = (List<string>)ViewData["AllStates"];
            }
            @Html.DropDownList("State", states.Select(s => new SelectListItem() { Text = s, Value = s, Selected = (s == Model.State) }))
        </dd>
    </dl>

    <p>
        <input type="submit" value="登録" />
        <input type="button" id="btnCancel" value="キャンセル" />
    </p>
    @Html.ValidationSummary("入力にエラーがあります。修正してください。")
}

<hr />

<p>
    <a href="/">業務メニューに戻る</a>
</p>


@section Scripts {

    <script type="text/javascript">
    $(function () {
        $("#btnCancel").click(function () {
            window.location = "@Url.Action("ListAuthors")";
            return false;
        });
    });
    </script>
}

上記コードの要点は以下の通りです。

  • モデルクラスの指定
    • ファイルの先頭に記述している “@model Decode2016.WebApp.Controllers.Sample02Controller.EditViewModel” が重要です。JavaScript エラーチェックロジックの動的生成機能を利用したい場合には、このモデルクラスの指定が必要になります。
  • 入力フォームの作成
    • データ入力フォームを作成するために、@using (Html.BeginForm(“EditAuthor”, “Sample02”, new { id = Model.AuthorId })) を使います。これにより、HTTP-POST でデータを送信するフォームを作成することができます。
  • 単体入力エラーチェック機能つきテキストボックスの作成
    • モデルクラスを指定した上で、@Html.TextBoxFor(m => m.XXX) という指定を行うことにより、入力エラーチェック機能つきテキストボックスを作成することができます。
  • 単体入力エラー一括表示部分の作成
    • @Html.ValidationSummary(…) 命令により、単体入力エラーを一括して表示する領域を作成することができます。

image

以上でクライアント側の実装は終わりです。続いて、登録ボタンを押下して HTTP-POST データを送信した際の、サーバ側の受け取りロジックを実装します。


[HttpPost]
public ActionResult EditAuthor(string id, EditViewModel model)
{
    using (PubsEntities pubs = new PubsEntities())
    {
        var query = pubs.Authors.Select(a => a.State).Distinct();
        ViewData["AllStates"] = query.ToList();
    }

    // 送信されてきたデータを再チェック
    if (ModelState.IsValid == false)
    {
        // 前画面を返す
        // ID フィールドがロストしているので補完する
        model.AuthorId = id;
        return View(model);
    }

    model.AuthorId = id;

    // データベースに登録を試みる
    using (PubsEntities pubs = new PubsEntities())
    {
        Author target = pubs.Authors.Where(a => a.AuthorId == model.AuthorId).FirstOrDefault();
        target.AuthorFirstName = model.AuthorFirstName;
        target.AuthorLastName = model.AuthorLastName;
        target.Phone = model.Phone;
        target.State = model.State;

        pubs.SaveChanges();
    }
    // 一覧画面に帰る
    return RedirectToAction("ListAuthors");
}

コードの要点は以下の通りです。

  • ビューモデルによる送信データの受け取り
    • ブラウザから送られてくるデータは、public ActionResult EditAuthor(string id, string AuthorFirstName, string AuthorLastName, string Phone, …) などのようにして、パラメータを使って受け取ることができます。(これをパラメータバインディングと呼びます)
    • しかし、上記のコードに示すように、構造体クラスを使って一括して受け取ることもできます。(これをモデルバインディングと呼びます)
  • モデルバインディングによる単体入力エラーチェック
    • モデルバインディングを使ってデータを受け取った場合、データアノテーションで指定した単体入力チェックにエラーがあるか否かを、ModelState.IsValid メソッドで簡単に確認することができます。また、サーバ側で単体入力エラーが見つかった場合、ビューを使ってエラーメッセージつき画面を簡単に返すこともできます。

以上により、ViewModel クラスにデータアノテーションにより付与した単体入力エラーチェックロジックを、ブラウザ上での JavaScript チェックとサーバ側での再チェックの両方に利用したデータ更新アプリケーションを作成することができます。

image

なお、今回は話を簡単にするため、以下の 2 点については説明を割愛しています。興味がある方は、各自で調査・実装してみてください。

  • アンチリクエストフォージェリ対策
    • 現在の実装の場合、サーバ側ではねつ造されたフォーム送信データも受け取って処理してしまいます。(いわゆる「なりすまし書き込み」ができてしまう)
    • この問題を避けるため、ASP.NET MVC ではアンチリクエストフォージェリ機能が備わっています。([ValidateAntiForgeryToken()]) 非常に簡単にこの機能を使うことができるようになっていますので、必ず追加で実装するようにしてください。
  • 楽観同時実行制御機能
    • 現在の実装では、データベース上のデータが他のユーザにより書き換えられたとしても、何も考えずに上書き更新してしまいます。
    • これを避けるために、通常は楽観同時実行制御機能による制御ロジックを組み込みますが、今回は簡単のため、これを実装していません。EF Core でも楽観同時実行制御機能の利用は可能ですので、こちらもぜひ組み込んでみてください。

引き続き最後のエントリでは、同じアプリケーションを Web API 方式(SPA 型)で開発してみたいと思います。

Part 7. ASP.NET Web API による SPA 型データ更新アプリ

$
0
0

いよいよ最後のエントリです。引き続き、Part 6. で作成したアプリを SPA 型で実装してみることにします。SPA 型でのデータ更新アプリ実装で厄介なのは、下図に示すように、クライアント側では JavaScript、サーバ側では C# を使って単体入力チェックロジックを実装する必要があり、うっかりすると二重実装になってしまう点です。

image

これに関しては、Part 6. で解説した、ASP.NET MVC のモデルバリデーション機能を活用するとうまく回避することができます。では、具体的な作成方法について解説します。

■ ファイルの配置

まずはコントローラクラス、ビュークラスのファイルをそれぞれ配置しておきます。(Part 6. で配置した、入力チェック用の JavaScript, CSS 定義も利用します。)

  • /Controllers/Sample03Controller.cs
  • /Views/Sample03/ListAuthors.cshtml
  • /Views/Sample03/EditAuthor.cshtml

image

■ 著者一覧ページの実装

まずは Part 5 で解説した方法をもとに、著者一覧ページを作成しましょう。 これについては特に説明は不要でしょう。

[/Controllers/Sample03Controller.cs]


using Decode2016.WebApp.Models;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Decode2016.WebApp.Controllers
{
    public class Sample03Controller : Controller
    {
        [HttpGet]
        public ActionResult ListAuthors()
        {
            return View();
        }

        [HttpGet]
        public List<AuthorOverview> GetAuthors()
        {
            using (PubsEntities pubs = new PubsEntities())
            {
                var query = pubs.Authors.Select(a => new AuthorOverview()
                {
                    AuthorId = a.AuthorId,
                    AuthorName = a.AuthorFirstName + " " + a.AuthorLastName,
                    Phone = a.Phone,
                    State = a.State,
                    Contract = a.Contract
                });
                return query.ToList();
            }
        }
    }
}

[/Views/Sample03/ListAuthors.cshtml]

@{
    ViewBag.Title = "編集対象の著者選択";
}
@section Libraries {
    @Html.Partial("_ImportsLibraryKnockout")
}

<h4>編集対象となる著者を選択してください。</h4>

<div class="table-responsive">
    <table id="tblAuthors" class="table table-condensed table-striped table-hover">
        <thead>
            <tr>
                <th>著者ID</th>
                <th>著者名</th>
                <th>電話番号</th>
                <th>州</th>
                <th>契約有無</th>
            </tr>
        </thead>
        <tbody data-bind="foreach: authors">
            <tr>
                <td><a data-bind="text: AuthorId, attr: { href: 'EditAuthor/' + AuthorId }"></a></td>
                <td data-bind="text: AuthorName"></td>
                <td data-bind="text: Phone"></td>
                <td data-bind="text: State"></td>
                <td>
                    <input type="checkbox" disabled data-bind="checked: Contract" />&nbsp;
                    <text data-bind="text: (Contract ? '契約あり' : '契約なし')"></text>
                </td>
            </tr>
        </tbody>
    </table>
</div>

<hr />

<p>
    <a href="/">業務メニューに戻る</a>
</p>

@section Scripts {
    <script type="text/javascript">
        $(function () {
            // ブラウザキャッシュ無効化 (これを入れておかないと、Edit ページから戻ってきたときにリストが更新されない)
            $.ajaxSetup({
                cache: false
            });

            $("#tblAuthors").hide(); // css('display', 'none') と同じ

            // 後から値を入れたい場合には、ko.observable() と ko.observableArray() を割り当てておく
            var viewModel = {
                states: ko.observableArray(),
                authors: ko.observableArray()
            };
            ko.applyBindings(viewModel);

            $.getJSON("/Sample03/GetAuthors", null, function (result) {
                viewModel.authors(result); // データを observableArray に流し込み
                $("#tblAuthors").show(); // css('display', 'block') と同じ

                console.dir(result); // 全体表示
            });

        });
    </script>
}

引き続き、SPA 型の編集ページを作成します。

■ 編集ページの作成

一般に、SPA 型で Web アプリを作成する場合、サーバ側からは静的な Web ページを返します。しかし、データ入力チェック用の JavaScript を含む Web ページを作成するのは面倒なため、Part 6. で解説した、@model + @Html.TextBoxFor(m => m.XXX) を利用して、動的に C# ViewModel クラスから JavaScript チェックロジックを生成してクライアントに送り返します。

image

ただし、以下の点に注意してください。

  • 実際に画面に表示するデータは、HTTP-GET で画面を取り寄せたのち、ASP.NET Web API 経由でサーバから取り出します
  • また、登録ボタンを押した際のデータ送信は、通常の form submit ではなく、JavaScript による制御を行います

では、段階的に実装していきましょう。まず、Sample03Controller.cs クラスの内部クラスとして、UpdateAuthorRequest クラスを追加します。

public class UpdateAuthorRequest
{
    [Required]
    [RegularExpression(@"^[0-9]{3}-[0-9]{2}-[0-9]{4}$")]
    public string AuthorId { get; set; }

    [Required(ErrorMessage = "著者名(名)は必須入力項目です。")]
    [RegularExpression(@"^[\u0020-\u007e]{1,20}$", ErrorMessage = "著者名(名)は半角 20 文字以内で指定してください。")]
    public string AuthorFirstName { get; set; }

    [Required(ErrorMessage = "著者名(姓)は必須入力項目です。")]
    [RegularExpression(@"^[\u0020-\u007e]{1,40}$", ErrorMessage = "著者名(姓)は半角 40 文字以内で指定してください。")]
    public string AuthorLastName { get; set; }

    [Required(ErrorMessage = "電話番号は必須入力項目です。")]
    [RegularExpression(@"^\d{3} \d{3}-\d{4}$", ErrorMessage = "電話番号は 012 345-6789 のような形式で指定してください。")]
    public string Phone { get; set; }

    [Required(ErrorMessage = "州は必須入力項目です。")]
    [RegularExpression(@"^[A-Z]{2}$", ErrorMessage = "州は半角大文字 2 文字で指定してください。")]
    public string State { get; set; }
}

Part 6 で使った EditViewModel クラスとほぼ同じですが、以下の点が異なります。

  • この構造体は、入力フォームではなく、ASP.NET Web API で受け取るデータ構造に合わせて作ります
  • 著者 ID は画面上での入力項目ではありませんが、ASP.NET Web API で受け取るデータ項目であるため、サーバ側での単体入力チェックが必要です。このため、データアノテーションによるチェックロジックを付与してあります。

続いて、ビューを送出するコード、及び画面に表示する著者データを送り返す Web API をコントローラクラスに作成します。

[HttpGet]
public ActionResult EditAuthor(string id)
{
    if (Regex.IsMatch(id, @"^[0-9]{3}-[0-9]{2}-[0-9]{4}$") == false) throw new ArgumentOutOfRangeException("id");
    ViewData["AuthorId"] = id;
    return View(new UpdateAuthorRequest());
}


[HttpGet]
public Author GetAuthorByAuthorId(string authorId)
{
    if (Regex.IsMatch(authorId, @"^[0-9]{3}-[0-9]{2}-[0-9]{4}$") == false) throw new ArgumentOutOfRangeException("authorId");

    // 当該著者 ID のデータを読み取る
    Author editAuthor = null;
    using (PubsEntities pubs = new PubsEntities())
    {
        var query = from a in pubs.Authors
                    where a.AuthorId == authorId
                    select a;
        editAuthor = query.FirstOrDefault();
    }
    return editAuthor;
}

続いて、ビューを実装します。とりあえず、画面上にデータを採ってきて表示するところまで。

@model Decode2016.WebApp.Controllers.Sample03Controller.UpdateAuthorRequest
@{
    ViewBag.Title = "著者データの編集";
}
@section Libraries {
    @Html.Partial("_ImportsLibraryValidation")
}

@section Styles {
    @Html.Partial("_ImportsStyleValidation")
}

<h4>著者データを修正してください。</h4>

<form id="frmInput">
    <dl>
        <dt>著者ID</dt>
        <dd>@ViewData["AuthorId"]</dd>
    </dl>
    <dl>
        <dt>著者名(名)</dt>
        <dd>@Html.TextBoxFor(m => m.AuthorFirstName, new { data_val_specialnamecheck = "指定された名前(名・姓の組み合わせ)は使えません。" }) @Html.ValidationMessageFor(m => m.AuthorFirstName, "*")</dd>
    </dl>
    <dl>
        <dt>著者姓(姓)</dt>
        <dd>@Html.TextBoxFor(m => m.AuthorLastName, new { data_val_specialnamecheck = "指定された名前(名・姓の組み合わせ)は使えません。" }) @Html.ValidationMessageFor(m => m.AuthorLastName, "*")</dd>
    </dl>
    <dl>
        <dt>電話番号</dt>
        <dd>@Html.TextBoxFor(m => m.Phone) @Html.ValidationMessageFor(m => m.Phone, "*")</dd>
    </dl>
    <dl>
        <dt>州</dt>
        <dd><select id="State" name="State"></select></dd>
    </dl>

    <p>
        <input type="button" id="btnUpdate" value="登録" />
        <input type="button" id="btnCancel" value="キャンセル" />
    </p>
    @Html.ValidationSummary("入力にエラーがあります。修正してください。")

    <p id="lblErrorMessage" class="error">
    </p>
</form>

<hr />

<p>
    @Html.ActionLink("業務メニューに戻る", "Index", "Home", new { area = "Common" }, null)
</p>

@section Scripts {
    <script type="text/javascript">
        $(function () {
            var authorId = "@ViewData["AuthorId"]";
            var authorToEdit = null;

            $.getJSON("/Sample03/GetAllStates", null, function (result) {
                $.each(result, function () {
                    $("#State")
                        .append($("<option></option>")
                        .attr("value", this)
                        .text(this));
                });
                $.getJSON("/Sample03/GetAuthorByAuthorId", { authorId: authorId }, function (result) {
                    $("#AuthorFirstName").val(result.AuthorFirstName);
                    $("#AuthorLastName").val(result.AuthorLastName);
                    $("#Phone").val(result.Phone);
                    authorToEdit = result;
                    $("#State option[value='" + result.State + "']").attr("selected", true);
                });
            });

            $("#btnUpdate").click(function () {
                // 後述
            });

            $("#btnCancel").click(function () {
                window.location = "@Url.Action("ListAuthors")";
                return false;
            });
        });
    </script>
}

いくつかポイントがありますので解説します。

  • <form> タグについて
    • このページから実際にデータ更新を行う場合は、フォームによるデータ送信ではなく、jQuery を使って Web API へデータを送信することにより処理を行います。この部分だけ見ると <form> タグは不要です。
    • が、実際には単体入力エラーチェックに(内部的に)jQuery Validation を利用します。jQuery Validation では入力フォームが <form> タグで囲まれていることが必要なため、ダミーで <form> タグを差し込んでいます。
  • 登録ボタンについて
    • <form> の送信機能を利用するわけではないので、<input type=submit> ではなく、<input type=button> で定義してください。
  • JavaScript 内での authorId の拾い方について
    • 上記のコードでは、var authorId = “@ViewData[“AuthorId”]”; というコードを記述しており、サーバ側で MVC ビューページを作成する際に著者 ID を埋め込むようにしています。これは、厳密にいえば SPA 型の作り方としては不完全です(URL から取り出して使うのが正しい)。
    • ……が、クライアント側 JavaScript チェックロジックの生成に MVC ビューページを使っているので、ここだけ頑張ってもあまり意味がありません;。なので、サーバ側で MVC ビューページを作成する際に著者 ID を埋め込む形で十分だと思います。

では最後に、サーバ側へのデータ送信処理を記述します。ASP.NET Web API では、MVC と同じモデルバインディングとそれによる単体入力エラーチェック機能を利用することができます。このため、以下のようにすれば簡単にサーバ側でのデータ再チェックを行うことができます。(※ サーバ側でのエラー発覚時は不正クライアントからのアクセスとみなせるため、例外を発生させて処理を止めてしまえば十分です。)

[HttpPost]
public void UpdateAuthor(UpdateAuthorRequest request)
{
    if (ModelState.IsValid == false) throw new ArgumentException();

    // データベースに登録する
    using (PubsEntities pubs = new PubsEntities())
    {
        Author target = pubs.Authors.Where(a => a.AuthorId == request.AuthorId).FirstOrDefault();
        target.AuthorFirstName = request.AuthorFirstName;
        target.AuthorLastName = request.AuthorLastName;
        target.Phone = request.Phone;
        target.State = request.State;
        pubs.SaveChanges();
    }
}

一方、クライアント側については、ボタン押下時に自力で jQuery Validation を呼び出して入力フォームをチェックし、そのうえでサーバに対してデータ送信するようなコードを記述します。(※ エラーサマリメッセージのクリアが厄介ですが、これは ASP.NET MVC の入力検証機能が Web API/SPA 型アプリでの利用を想定していないためです。なのでここはハックする形での実装になります。)

$("#btnUpdate").click(function () {
    if ($("#frmInput").valid() == true) {
        // エラーサマリメッセージをクリア
        $("#frmInput").find("[data-valmsg-summary=true]").removeClass("validation-summary-errors").addClass("validation-summary-valid").find("ul").empty();

        $.post(
            "/Sample03/UpdateAuthor",
            {
                AuthorId: authorId,
                AuthorFirstName: $("#AuthorFirstName").val(),
                AuthorLastName: $("#AuthorLastName").val(),
                Phone: $("#Phone").val(),
                State: $("#State").val()
            },
            function (result) {
                window.location = "@Url.Action("ListAuthors")";
            }
        );
    }
});

以上でアプリを動作させると、SPA 型でも単体入力チェックの二重実装を回避することができます。

image

■ 参考&補足事項

なお、今回は話を簡単にするため、ASP.NET MVC と同様、以下の 2 点については説明を割愛しています。興味がある方は、各自で調査・実装してみてください。

  • アンチリクエストフォージェリ対策
    • ASP.NET Web API の場合であっても、やはりアンチリクエストフォージェリ対策をしておくことが望ましいです。
    • 上記のコードからわかるように、ASP.NET Web API の内部動作は、ほとんど ASP.NET MVC と同じです。よって、アンチリクエストフォーじぇり機能も Web API で転用することが可能です。
  • 楽観同時実行制御機能
    • ASP.NET MVC 同様、Web API の場合でも楽観同時実行制御について考えることが望ましいですが、ちょっと厄介なのは、特に SPA 型でアプリを作る場合、更新前の状態のデータをどこにどうやって残すのか、という点です。
    • ASP.NET MVC ではサーバ側に Session オブジェクトで残す、という実装方法が最も簡単ですが、Web API で同じことをすると、サーバ側 API をステートレスにすることができません。このため、基本的にはクライアント側で、更新前の状態を残しておき、これをサーバに(更新したデータとともに)渡して処理してもらう必要があります。
    • 具体的なやり方としては、シリアル化したデータを Base64 エンコードしてクライアント側に渡しておき、それをサーバ側に再送してもらってデシリアル化して使う、という方法になりますが、シリアル化/デシリアル化などの処理を自作しなければならないこともあり、ちょっと面倒です。とはいえ実際の開発では必要になるので、力試しも兼ねて頑張って実装してみてください。

また上記の実装では、ASP.NET MVC の入力検証機能を ASP.NET Web API に転用しているのですが、もともとこの方法は非サポートな方法です。このため、現在のバージョンでは動作しますが、今後の実装でも動き続けるかどうかはわかりません;。ただ、今回それでも敢えてこの実装方法を示したのは、実際のシステムで更新系アプリを作る場合、サーバ/クライアントでのロジック二重実装をどうやって避けるのかは必ず考えなければならない問題であるからです。これに対する考え方は何通りかに分かれます。

  • 上記のような実装を、自己責任で行う。
  • 3rd party 製ライブラリ(SPA ライブラリ)でこの問題を解決するものを探して使う。
  • あきらめて、サーバ側とクライアント側を二重実装する。
  • 更新系アプリについては、SPA 型での実装をあきらめ、Part 6 で示した MVC 型での実装にする。(=参照系アプリのみ SPA 型で実装する)

実際には一番最後の手法も一考の余地があります。というのも、SPA 型アーキテクチャにしてリッチにしたい UI の多くは、参照系であることが多いためです。この辺は、実際に開発しようとするアプリによって最適な選択肢が変わると思いますが、いずれにしても無理に SPA 型実装にこだわりすぎないようすることも、業務アプリ開発の場合には重要です。全体のバランスを見ながら、最適な実装指針を決めていただければと思います。

■ さらに学習を進めたい方へ

……というわけで、ASP.NET Core 1.0 を使った Web アプリの開発手法についてここまで解説してきました。今回はあくまで入門編、ということで、わかりやすい実装を心がけていますが、実際のシステム開発では DI コンテナを活用するなど、いろいろ知っておくと便利なテクニックもたくさんあります。さらに ASP.NET Core の学習を進めたい場合には、以下のようなリソースをぜひ参照してみてください。

  • ASP.NET Core Documentation
    • https://docs.asp.net/en/latest/
    • ASP.NET Core の公式ドキュメント。最も詳しくてわかりやすいので、詰まった場合は必ずここをチェック。
  • GitHub ASP.NET
    • https://github.com/aspnet
    • ASP.NET Core のソースコード。この中にテストコードが含まれており、それを見るとライブラリの使い方が確認できたりする。
    • ロードマップ情報や設計・実装に関する議論もすべてここでオープンになって開発されているため、突っ込んだ学習がしたい場合にはここを参照するとよい。

Part 1. Windows 10 の導入と WaaS モデルへの対応

$
0
0

[Windows 10 の現状]

Windows 10 は 2015/07/29 にリリースされていますが、この blog を書いている 2016 年 8 月時点では、特にエンタープライズ系企業での導入はまだ十分に進んでいるとは言えません。大きな背景としては、日本の大企業ではハードウェアの老朽更新に併せて OS 入れ替えを行うことが多いこと、ハードウェアの買い替えサイクルが諸外国より長いことがあるのですが、とはいえ、以下のようないくつかの要因から、昨今、次期端末の購入・導入計画に併せて Windows 10 の導入計画を進めているお客様が非常に増えてきています。

  • Windows 10 のリリースから約 1 年間が経過している(のでそろそろ安定しているだろうw)
  • 昨今のセキュリティ脅威に対する安全性を高めるために OS を最新化したい
  • 多彩な Windows 10 デバイスとモバイルワークスタイルで、社員の生産性を高めたい

image image

今年から来年にかけて多くの企業で Windows 10 の導入が進むことを願っているのですが、注意すべき点として、Windows 10 の導入は、単に OS を入れ替えればよい、というものではありません。必ず WaaS (Windows as a Service)モデルへの適応方法を検討しておく必要があります。

[WaaS とは何か]

WaaS(Windows as a Service)とは、OS のアップデートに関して、従来のような「数年に一度の大型アップデート」というモデルをやめ、「より頻繁に、小さな改善を積み重ねていく」というモデルに変え、オンラインでいち早く皆様に提供していこう、というものです。実際、Windows 10 は 2015/07/29 の提供後、すでに 2 回の機能アップグレードを行っており(November Update 及び Anniversary Update)、アップグレードの提供頻度が従来と大きく変わっていることがわかります。

この WaaS というモデルを Windows OS に導入した理由・背景はいくつかあります。代表的なものは以下の 3 つです。

  • IT のコンシューマ化
    • 昨今、我々は iPhone, iPad, Android (もちろん Windows Mobile も!)をはじめとするスマートデバイスを多用しています。こうしたスマートデバイスの世界では、OS を最新化して使うことは常識と化しています。
    • 例えば iOS の場合、OS シェアが開発者向けページに掲載されていますが、ほとんどのデバイスが最新 OS を使っていることがわかります。これは、会社に戻ると依然として 2 世代前の Windows 7 を使っている……という Windows PC の世界とは全く違う世界ですが、今のスマホネイティブ世代にとっては、PC の世界はいかにもレガシーです。
  • セキュリティ脅威の進化
    • 詳細は次のパートで解説しますが、近年は民間企業を対象にしたサイバー攻撃が激化しており、その攻撃手法の進化はすさまじいものがあります。こうした攻撃手法の進化に追随するためには最新の対策が必要であり、そのためには必然的に OS の最新化が必要になります。
    • この話を聞くと、「古い OS でもセキュリティパッチを全部当てればいいんでしょ?」と単純に考える方も少なからずいますが、セキュリティパッチというのはその名の通り、脆弱性の穴を塞ぐためのものです。抜本的に、仕組みレベルからの安全性の強化を行うためには、どうしても OS やハードウェアの最新化が必要になります。
  • テクノロジーの進化
    • ご存じのように、昨今のハードウェア技術とソフトウェア技術はどんどん進化しており、利用者の生産性を高める最新技術が次々と出てきています。
    • こうしたものを貪欲に取り込んでいかないと、新しいサービスをお客様に提供していくことは困難です。

こうした時代背景を元に、Windows 10 では、新しい機能(機能追加や機能強化)を、いち早く、オンラインでのアップデートとしてこまめに提供していくこととなった、というのが WaaS(Windows as a Service)です。

image

[Windows 10 導入時に考えるべきこと]

このことからわかるように、Windows 10 への移行というのは、言い換えれば、継続的に最新化されていく IT インフラへの移行である、ということができます。このため、従来とは異なる考え方やアプローチが必要になる部分も多々出てきます。そのために何を理解し、何を考えていけばよいのか? を整理すると、以下のようになります。

  • Part 2. 代表的な Windows 10 導入の目的
    • Windows 10 の導入を始めるにあたって、どのようなメリットを得るのかを予め検討しておく必要があります。
    • これを決めておかないと、そもそも Windows 10 導入の予算が取れなかったり、Windows 10 導入時にどこを重点的に検討すべきなのかが決められません。
  • Part 3. WaaS の正しい理解
    • 「継続的に最新化される」といっても、どのような形でどんなアップグレードやアップデートがどのような頻度で降ってくるのか、を理解しておく必要があります。
    • この際、機能アップグレードの受け取り方の選択肢(IP/CB/CBB/LTSB)とその使い分けが極めて重要になるため、これを理解します。
  • Part 4. Windows 10 導入時の検討の流れ
    • Windows 10 の導入では、アプリ/インフラの両面での検討が必要になり、またすぐに対応できること/できないことが出てきます。
    • これらをどのように組み立てて導入検討を進めるべきなのかを理解します。
  • Part 5. Windows 10 導入時に考え方・やり方を変えるべきポイント – アプリ編
    • アプリ面では、「半年に一度の互換性検証」をどのように効率的にやるかを考えなければなりません。
    • このための基本的な考え方である、① テストケース絞り込み、② 段階展開によるリスクヘッジ、を理解します。
  • Part 6. Windows 10 導入時に考え方・やり方を変えるべきポイント – インフラ編
    • インフラ面では、過去の「負の遺産」ともいうべき旧態依然としたやり方を見直すことが重要です。(パラメータシートとか;)
    • Windows 10 導入時に、従来と特に考え方が変わるポイントを理解します。
  • Part 7. Windows OS の変更ログの入手方法
    • アプリ/インフラどちらに関しても、OS 更新時に「何が変わったのか?」を把握することが必要になります。
    • この情報の入手方法を理解します。

なお、皆様が IT Pro なのか Dev なのかによって、興味のあるパートが異なると思いますが、どちらであっても、可能な限りすべてのパートにざっと目を通してください。というのも、Windows 10 WaaS では、インフラとアプリが互いに影響を及ぼしうる部分があるため、互いのことを知らないと正しいアプローチができないからです。細かく理解しなくても構いませんので、とにかくざっと要点だけつかむようにしていただくことをオススメします。

(以下は余談です)

実は上記のポイントは、結構マイクロソフトの社内でも問題になっている部分ではあったりします。というのも、例えば Windows OS の展開支援はインフラ系コンサルのみで行うのがかつての常識だったのですが、Windows 10 の WaaS に関しては継続的なアプリ互換性検証が問題になるため、私のようなアプリ系のコンサルが関わらなければならないケースというのが増えているのです。要点さえ理解できれば実は全く難しくないのですが、IT Pro であってもある程度はアプリのことを知らなければならないし、 Dev であってもある程度は WaaS のことを知らなければ、これからの時代は通用しなくなってくる、ということだと思います。ぜひ一連のエントリを読んでいただいて、最低限知っておくべき知識を身に着けていただければと思います。

Part 2. 代表的な Windows 10 導入の目的

$
0
0

[Windows 10 導入目的の明確化の必要性]

Windows 10 を導入する目的は、おそらくお客様ごとで様々に異なるでしょう。ハードウェアサポート切れなどの消極的な理由で Windows 10 を導入する、という場合もあるでしょうし、一方で積極的に Windows 10 の最新技術を使いたい、という場合もあるかもしれません。ただ、いずれの場合であっても、Windows 10 の導入を始めるにあたって、どのようなメリットを得るのかを予め検討しておかないと、そもそも Windows 10 導入の予算が取れなかったり、Windows 10 導入時にどこを重点的に検討すべきなのかが決められません。

Windows 10 導入の代表的な目的としては、以下の 3 つが挙げられます。いずれも IT インフラ管理者の目線ではなく、「お客様目線(エンドユーザ目線)」でのメリットですが、こうした目線で訴求ポイントをまとめておくと、その後の検討や提案も進めやすくなると思います。

image

以下、順番に見ていきたいと思います。

[1. セキュリティ強化]

サイバー攻撃は直近 10 年で大きく変化しています。従来は単なるいたずらを動機としていたものが、現在では利益やテロが目的となっており、その攻撃規模も従来とは比べ物にならないほど大きくなっています。

image

恐ろしいことに、現在ではゼロデイ脆弱性(パッチが提供されていない脆弱性)を売買する企業が存在していたり、さらにそれを元にマルウェアを開発するためのツールキットも存在しており、超有能なクラッカーでなくてもサイバー攻撃ができる環境が整いつつあります。サイバー攻撃による ROI は 1000% 以上などとも言われており、さらには企業の約 9 割は未知の脅威が侵入済みであるといった話まで存在します。

この辺については、de:code 2016 のセキュリティ関連セッションが詳しいため詳細はそちらに譲りたいと思いますが、重要なポイントとして、最新の攻撃への一番効果的な対策は、最新の OS への乗り換えである、という点を理解しておく必要があります。

  • 典型的な誤解として、セキュリティパッチをすべて当てさえすれば大丈夫、というものがあります。もちろん、セキュリティパッチを適切に当てていくことは基本中の基本ですが、これはあくまで「現在の『穴』を塞ぐ」というものでしかなく、「根本的な安全性を高める」ことはセキュリティパッチではできない、という点を理解する必要があります。
  • 例えば Credential Guard という仮想化ベースセキュリティの仕組みは、Pass-the-Hash と呼ばれる攻撃への安全性を大きく高めますが、これは内部的に Hyper-V を使って「やり方そのもの」を変えています。パッチだけでどうにかなるものではありません。
  • 例え話ですが、車の安全性を高めるためにエアバックを取り付けよう……というときに、50 年前の車に取り付けることはまず無茶でしょう;。でも、最新の車であれば、エアバックなんて当然のように標準装備です。

一例として、米国防総省は、400 万台の PC を Windows 10 にアップグレードする、という基本方針を決めました。仔細はリンク先を見ていただけるとよいと思いますが、その際の決定のキーポイントは以下の 2 点だったそうです。

  • 最新・最高レベルのセキュリティを持つ OS の活用
  • 単一のプラットフォーム利用により、運用環境の合理化とコスト削減を同時に実現

Windows 10 やマイクロソフトの製品が安全なのかどうか? という点については人により意見が分かれるところでしょうが、ただ、米国防総省は全世界で最もサイバー攻撃を受けているといわれており(ちなみに二番手はマイクロソフトだそうです;)、そうした組織が上記のような判断をしている、というところはひとつ参考になる部分だと思います。

[2. モバイルワークスタイル]

日本の労働人口の生産性は、先進諸国の中では群を抜いて低い、という話を聞かれたことがある方も多いかと思います。実際、仕事がとにかく大変、と実感されているサラリーマンの方は非常に多いのではないかと思いますが、今のやり方を変えなければ、その延長線上にある未来はとても明るいとは言えないでしょう。今後の日本は少子高齢化により、社会保障費の増大・貯蓄率低下・投資率低下などが進むことが見えており、従来のような労働集約型産業は成立しなくなっていきます。となると、必然的に、老若男女が共に働き、高い生産性で(=短時間で)稼ぎ、さらにに多様な働き方を認めることが必要になってくるはずです……が、日本は特にホワイトカラー(知的労働者)の生産性が依然として低いのが実態です。(これに関しては、樋口さんのこちらのエントリが非常にわかりやすいです。一読の価値があるかと。)

ホワイトカラーの生産性を高める手法は様々にありますが、その一つとして注目されているのが、モバイルワークスタイルと呼ばれる考え方です。簡単に言えば、タブレット PC とモバイルルータとスマホ、そしていつでもどこでも使えるアプリを使って、いつでもどこでも仕事ができるようにすることで、1 日 24 時間をより柔軟かつ効率的に使いましょう、というワークスタイルです。

image

このワークスタイルは、例えばマイクロソフトの社員はまさに日々実践しており、私自身も正直これがなければ生きていけない、と実感するほど極めて重要なものになっています。例えば私の場合、少し前に息子が生まれたのですが、

  • 朝は少し家事を手伝いつつ頭の中で情報を整理し、自宅である程度仕事をこなして作業が行き詰まったところでラッシュアワーを避けて出社。
  • 電車に乗りながらアイディアを練り込み、会社についたら資料作成を継続。
  • そして早めに自宅に帰って子供をお風呂に入れ、家族で食事を取ってから再び仕事に戻る。

なんていうワークスタイルを取っているのですが、こういうワークスタイルが取れるからこそ日常を回していける、という実態があります。

こうしたモバイルワークスタイルの話をすると必ず出てくるのが、人や制度に関わる課題、あるいは情報漏えいなどに関わるセキュリティ上のリスクなどです。これらの懸念があるのは当然ですし、それをヘッジするためにルールを設けるのは当然のことですが、必要以上にがんじがらめにして社員の生産性を損なうようでは本末転倒です。(概してルールを決める人がリスクを取りたくないが故に、がんじがらめにしすぎる傾向がありますが、この点については意思決定者の方にはホントに考えてほしいところです、いやマジで;。)

image

特にセキュリティに関しては後ろのパートでも触れていきたいと思いますが、近代的なセキュリティの考え方は、「安全性と利便性の両方を取る」「ルールを増やすのではなく技術をうまく活用する」のが基本です。最も高度なセキュリティを要求される銀行でも、従業員の業務効率化やワークスタイル変革に向けた動きを始めているほどで、例えば三井住友銀行様では Windows 10 とパブリッククラウドを活用した改革へと舵を切っています(情報はこちら)。Windows 10 の導入は非常によいタイミングでもあるので、ワークスタイル変革についてもぜひ検討してみてください。

[3. 多彩なデバイス活用]

前述したモバイルワークスタイルはユビキタスコンピューティングとの両輪で実現されるもので、豊富なクラウドサービス、いつでも利用可能な高速モバイル回線、そして多彩なデバイスによって下支えされています。マイクロソフトもこうした「多彩なデバイス」の重要性を認識しており、現在ではウェアラブルから大型デバイスまでを、Windows 10 というシングル OS でサポートする、という環境を実現しています。

image

こうした多彩なデバイスを、状況に応じて使い分けたり併用したりすることが、特にモバイルワークスタイルなどでは必要になってきますが、この際、マルチデバイスに対する重複開発の問題をいかに最小限に抑え込むかが重要になります。この点に関しては、UWP(Universal Windows Platform)を使えば、複数の Windows 10 デバイスに対して一つのバイナリで対応するアプリケーションの開発ができる、というポイントが大きなメリットになります。(もちろん、複数のスクリーンサイズに対応するためのレスポンシブ対応などの工夫は必要になりますが、開発技術が同じであり、ロジックなどを使いまわせるメリットは非常に大きいです。)

※(注記) 少し話が逸れますが、マルチデバイスアプリなら UWP ではなく HTML5 での開発ではないか? と考える方も稀にいますが、HTML5 ではネイティブアプリ開発技術ほどの開発生産性や操作性を実現できません。例えば UWP, Xamain, HTML5 の 3 つの技術を比較すると、これらは開発生産性やユーザビリティと、リーチ性の間でトレードオフの関係を持ちます。ここではこれ以上深掘りはしませんが、なんでも HTML5 で作ればよい、という発想は、業務アプリ開発の世界では必ずしも正しくないことに注意してください。

こうした多彩なデバイスの活用は、ビジネスのやり方をも変える力を持ち得ます。下図はデバイスの活用による業務改革の例ですが、① 社員の生産性の改善、② オペレーションコストの低減、③ お客様サービスの改善、といったシナリオでデバイス活用を考えてみると、様々なアイディアが出てくるのではないかと思います。Windows 10 の導入に併せて、モバイルワークスタイルだけでなく、こうした業務改善・改革も検討してみていただくとよいでしょう。

image

ここでは、代表的な Windows 10 導入の目的を 3 つ例として挙げましたが、いずれにせよ、こうした訴求メットを明らかにしておかないと、せっかくの Windows 10 の導入も「単なる OS 入れ替え」だけに留まってしまいます。新しい OS を入れるのであれば、新しいメリットをぜひ入れられるよう検討してみてください。


Part 3. WaaS の正しい理解

$
0
0

さて、ここからいよいよ本題である WaaS の解説に移っていきます。

[WaaS の正しい理解 – Windows 10 における OS 更新モデル]

ここまで解説したきたように、「Windows 10 の導入」とは、「継続的にアップグレードされ続ける環境への移行」でもあります。このため、Windows 10 の導入に際しては、『導入後にどのようになるのか?』を理解・検討しておくことが極めて重要です。まずは、Windows 10 移行後の、WaaS による継続的更新(機能追加)の仕組みについて理解しましょう。

image

[4 つの OS 更新モデル(IP, CB, CBB, LTSB)]

ご存じのように、Windows には Home, Enterprise, Pro などの「エディション」という分類が存在しますが、これとは別にもう一つ重要な分類として、「機能追加の受け取りタイミング」という選択肢が存在します。例えばこれまで、Windows 10 は November Update、Anniversary Update という機能追加が実施されているのですが、この機能追加をいつのタイミングで受け取るのかを調整することができるようになっています。具体的には、以下の 4 つの選択肢があります。この 4 つの選択肢については(名称も含めて)暗記してください。

名称 この資料での略称 概要 主な利用者
Insider Preview IP 機能追加の受け取り時期を早める 一部の早期利用ユーザ
Current Branch CB 4~8 ヶ月間隔で OS の機能追加を行う 一般のコンシューマユーザ
Current Branch for Business CBB 機能追加の受け取り時期を 4 ヶ月間 遅らせる 一般的なビジネスユーザ
Long-term Servicing Branch LTSB 機能追加を一切受け取らない 特定用途の固定端末

これらの考え方ですが、CB を基準にして考えると非常にわかりやすいです。

  • CB : コンシューマユーザには、約 4~8 ヵ月の間隔で、OS の機能追加が行われる
  • IP, CBB : 機能追加の受け取り時期を、ある程度、早める or 遅らせることができる
  • LTSB : 特定用途の端末向けに、機能追加を一切受け取らない方式も提供される

企業内では、用途に応じてこの 4 つの選択肢を使い分ける必要があります。すなわち、以下のようになります。

  • 一般的なビジネスユーザ(大多数の従業員)は CBB を利用することで、コンシューマ市場で「4 ヶ月間揉まれて安定性を高めた」 Windows OS を利用することができる。
  • パワーユーザーは、CB や IP を利用することで、早い段階で OS の最新機能を使ったり試したりすることができる。
  • 一方で、特定用途の固定端末では、LTSB を使うことで、「ずっと変わらない Windows」を使うことができる。

 

image

なお、LTSB に関しては一般的な OA 端末での利用を想定していないため、利用に際しては十分な注意が必要です。このため、LTSB に関しては IP/CB/CBB とは分けてこのエントリの後ろの方で詳しく解説します。

上に示した IP/CB/CBB の違いをより詳しく理解するために、ここまでの Windows OS のリリースの流れを見てみると、次のようになります。

image

要点は以下の通りです。

  • まずは CB のリリースの流れに着目すると理解しやすい
    • 従来のサービスパックに相当する「機能アップグレード」が 4~8 ヶ月おきに登場
      • 当初想定では 4 ヶ月おきだったが、実際には約半年に 1 回程度のペースで機能アップグレードが行われている
      • この「機能アップグレード」により、機能の追加・改善が行われる(例:Cortana など)
  • これに対して、IP を利用すると、開発中の β 版を先行して利用できる
  • 逆に CBB を利用すると、 4 ヶ月間コンシューマ市場で揉まれた後の、より安定化した OS を利用することができる

すなわち、CBB = CB + 4 ヶ月分の累積パッチ、であり、CB/CBB の機能的差異はありません。ここは非常に重要なポイントなので、必ず押さえておいてください。

※ また、上記の各機能アップグレードのコード名とバージョン番号/ビルド番号についても覚えておくとよいでしょう。マイクロソフトの資料では、これらの名称がよく出てくるためです。

  • 無印(初回リリース):TH1, 1507, 10240
  • November Update : TH2, 1511, 10586
  • Anniversay Update : RS1, 1607, 14393

[IP/CB/CBB の切り替え方法]

上で述べたように、IP/CB/CBB の違いは、機能アップグレードの受け取りタイミングの違いにあります。IP/CB/CBB は、OS の設定オプションから切り替えることができます。

image

また、Insider Preview に関しては、受け取り頻度を Fast/Microsoft/Slow などの中から選択することができるようになっています。Insider Preview は開発中の中間ビルドを受け取るオプションであり、場合によっては極めて不安定なビルドを受け取ってしまうことがあるためです。中間ビルドを受け取るときでも、ある程度安定したもののみを受け取りたい場合には、Slow などを選択していただけるとよい、ということになります。(ちなみに最も展開が早いビルドはカナリアビルドと言われていますが、これはさすがにマイクロソフト社内の R&D 内部のみでしか入手することができません。私も受け取れません;。)

image image

ここは重要なポイントですが、Windows 10 では、ビジネスユーザーが受け取る OS(すなわち CBB)がなるべく安定したものになるよう、OS の利用者を段階的に増やしていっている、という形になっています。ある意味、Insider Preview の参加者は一般コンシューマユーザに対する人柱であり、そして一般コンシューマユーザはビジネスユーザの人柱である、という構図が読み取れるわけですが;、このように、段階的な展開の仕組みを使うことで、最終的に安定したものを大多数のユーザーに届けられるようにする配信方式の考え方を、リング配信(Ringed Deployment)と呼びます。このリング配信の考え方は、アプリ互換性検証においても極めて重要なポイントになるため、しっかり理解しておいてください。

[IP/CB/CBB/LTSB と Home/Enterprise/Pro/Ent-LTSB の関係]

さて、ここまで機能アップグレードの受け取りタイミングについて解説してきましたが、OS のエディション(Home/Enterprise/Pro)により、利用できる受け取りタイミングオプションが異なります。具体的には下図の通りです。

image

ポイントは以下の通りです。

  • Home エディションでは CBB は使えない(=機能アップグレードの受け取りタイミングを遅らせることはできない)。
    • 理由は……上に書いた話から察してくださいw。
  • LTSB は特殊なエディションのため、異なるインストールメディアが必要になる。
    • Home/Enteprise/Pro のオプション変更では、LTSB にすることはできません。LTSB を使いたい場合には、Enterprise-LTSB と呼ばれる特殊なインストールメディアが必要です。
    • 例えば下図は MSDN サブスクライバダウンロードの画面ですが、この中の “Windows 10 Enterprise 2015 LTSB” と書かれているものが Enterprise-LTSB エディションに該当します。
      image

[企業内における機能アップグレードのタイミングの考え方]

次に、企業内ではどのタイミングでどのように OS をアップグレードしていくべきなのか、という点について考えてみます。下図は現在までの Windows 10 の機能アップグレードのリリースタイミングを示したものです。

image

まずはこの図の要点をいくつか説明します。

  • 機能追加・機能変更は、Insider Preview の期間中(図中の “Evaluate” の期間中)しか行われない。
    • 一度 CB がリリースされると、以降は脆弱性・信頼性などの修正(パッチ)しか出ない。
    • このため、CB/CBB 間には機能的差異はない
  • CB リリース後、4 ヶ月後にそれが CBB 扱いとなる。
    • 名目上の取り扱いが変わるだけなので、インストールメディアなどが入れ替えになるわけではない。
    • ただし、インストールの手間を容易化するため、CBB 扱いになったタイミングで、累積パッチを取り込んだメディアが再作成されて提供されている。
    • (TH1 だけは初回リリースが CB 兼 CBB となっているが、これは例外的取り扱い)
  • 各機能アップグレード(TH1, TH2, RS1, RS2,…)に対するパッチ提供は、n+2 バージョンの CBB がリリースされるまで行われる
    • よって、どんなに遅くても n+2 バージョンの CBB がリリースされるまでに(脆弱性パッチが提供されなくなるので)アップグレードを行う必要がある。

さて、実際の企業内システムの場合に問題になるのは、CBB リリース後のいつのタイミングで新しい CBB にアップグレードするのか、です。例えば、業務アプリの互換性検証が終わっていない場合や一部のサードパーティ製アプリが新しい CBB のサポート表明をしていない場合、たとえ新しい CBB がリリースされてもアップグレードができないということが起こりえます。このため、グループポリシーを使うことで、通常は CB リリースから 4 ヶ月後とする機能アップグレードの適用を、さらに遅らせることができるようになっています

ただし(ここめちゃめちゃ重要です)注意していただきたいこととして、上記の機能アップグレードの適用遅延機能は、CBB の 1 バージョンスキップを認めるものではない(=必ずすべての CBB を順次適用していく必要がある)、ということを理解しておく必要があります。

  • 先に述べたように、各機能アップグレード(TH1, TH2, RS1, RS2,…)に対するパッチ提供は、n+2 バージョンの CBB がリリースされるまで行われます。
    • 例えば TH1 へのパッチ提供は、RS1 の CBB リリースと共に打ち切られることになります。
  • このことに関連して、パッチ提供期間を少し(60 日間)だけ延長する、という話があるのですが、仮にパッチ提供期間が延長されたとしても、CBB を 1 バージョンスキップする、という考え方は避けるべきです
    • 例えば下図のように n+2 バージョンの CBB がリリースされたら速やかに 1 バージョンスキップして機能アップグレードを適用する、という考え方を採るのは非現実的です。
    • image
    • もともとこの “60日間延長” は、パッチ提供期間を少しだけ伸ばしてもらうことで、CBB を 1 バージョンスキップして業務アプリの互換性テストを年 2 回程度から年 1 回程度に減らしたい、というお客様の要望から来ています。
    • しかしその一方で、上図のようにアップグレードを行おうとした場合、n+2 の CB リリース後、6 ヶ月以内に業務アプリや ISV アプリの対応を完全に済ませ、社内すべての OS を完全にアップグレードする必要があります。しかしこれはどう考えても極めてリスクが高く、現実的ではありません。 (特に ISV アプリのような、社外アプリの対応が間に合わないリスクがあります)
    • このため、n+1 の CBB リリースから n+2 の CBB リリース + 60 日間が、機能アップグレードの適用猶予期間である、と考えて、ひとつずつ CBB のアップグレードを繰り返していく必要があります。
    • image

というわけで、企業内における機能アップグレードの適用タイミングの考え方の基本について説明しましたが、実際のアップグレードタイミングについては業務アプリの互換性検証と強くかかわる部分がありますので、それについては Part 6 にて解説します。

[Windows 10 に対する「更新」の分類について]

さて、ここまで Windows 10 の機能アップグレード(November Update や Anniversary Update)について解説してきましたが、Windows 10 に対して行われる「更新」について、ここで整理しておきたいと思います。Windows 10 では、3 種類の「更新」が行われており、それぞれ名称が異なります。これらについては名称をしっかり覚えておいてください

  • ① 機能アップグレード(Feature Upgrades)
    • 従来のサービスパックに相当するもの。
    • スタートメニュー、設定画面、Cortana、Edge ブラウザなどの機能追加・変更が含まれる。
    • CB としてリリースれさる前に先行検証したい場合には、Insider Program に参加する。
  • ➁ サービス更新プログラム(Servicing Updates)(または 品質更新プログラム(Quality Updates/QU))
    • 従来の更新プログラムに相当するもの。
    • 脆弱性修正(セキュリティパッチ)が中心で、信頼性やバグに関する修正も含まれる。
    • 以下の 2 点に注意する。
      • OS Edge ブラウザに対する機能追加・変更は含まれない。
      • 従来と異なり、必ず累積パッチとして提供される(個別の適用はできない)
    • 配布前に先行検証したい場合には、SUVP(Security Update Validation Program)に参加する。
  • ③ ストアアプリの更新の配信
    • メールやカレンダアプリの更新は、①➁とは別に、Windows ストア経由で配布される

整理すると、以下のようになります。これを覚えておくとよいでしょう

配信内容 英語表記 簡単に言うと… 備考
① 機能アップグレード Feature Upgrades (FU) サービスパック OS や Edge ブラウザの更新
② サービス更新プログラム Servicing Updates (SU) 累積セキュリティパッチ 通常、毎月第二火曜日に配信
③ ストアアプリの更新の配信 メールやカレンダーアプリの自動更新 ①②とは無関係に配信

ちなみに細かい話で恐縮ですが、”Upgrade” と “Update” という用語が使い分けられていることに注意してください。FU は「アップグレード」、SU は「アップデート」です。(といいつつ、一般的に使われている November Update や Anniversary Update はいずれも FU なのに Update という名称になっているのはどうよ? という感じなんですけどね;、とグチってみる。)

なお、②に関しては RS1 以降は品質更新プログラム(Quality Updates, QU)の名称に変更される予定です。しばらくは SU/QU 両方の表記が出てくると思いますのでご注意ください。(この blog では SU で統一しています)

[LTSB (Long Term Servicing Branch)について]

さて、ここまで IP/CB/CBB を念頭に置いて解説を進めてきましたが、ここで LTSB について整理します。LTSB は、10 年間にわたりパッチのみが提供され続ける、非常に特殊なエディションで、その間、機能追加・変更は行われないというものです。今はまだしも、10 年後には超レガシー端末になるというもので、一般な OA 端末では使うべきではない特殊なエディションです。

もともと LTSB は、例えば製造ラインに据え付けで利用する制御用の PC や、キオスク端末のように特定の業務アプリだけしか動作させない PC での利用を想定しているもので、いわば組み込み用の Windows OS である、と理解するとわかりやすいです。インストールに利用する媒体も通常の Home/Enterprise/Pro とは別のメディアになっており、インストールした際の画面も大きく異なります(ストアもなければ Edge ブラウザもない)。ストアや Edge ブラウザが削られているのは、上記のような「ずっと変わらない端末」での用途を想定していることを念頭におけば当然のことと言えます。

image

LTSB に関しては、頻繁な(半年に一度の)アプリ互換性検証を回避するための魔法の杖であると誤解されているケースが非常に多いのですが、これは全くの誤りです。確かに LTSB を使えば機能追加・変更が行われなくなりますが、それは同時に技術革新が一切行われない、ということでもあります。10 年間同じ OS を使い続けるということは、言ってみれば 2016 年においてもまだ 2007 年にリリースされた Vista を使い続けている、というようなものです。

基本的に、一般社員向けの端末では、CBB を利用することをまず念頭に置いて考えてください。イメージとしては、Office を入れて使うような一般的な OA 端末は CBB を選択するのが基本、と考えるとわかりやすいでしょう。逆に、OS に新しい機能を取り込む必要のない特定用途の固定端末では、LTSB を使うことも検討していただければと思います。

# LTSB に関するこの誤解を誘発しているのはいくつかの原因がありますが、大きくは、① アプリ互換性検証に関して正しい理解や考え方がされていない、② LTSB にも MSI 版の Office がインストールできてしまう、③ LTSB → LTSB のアップグレードがサポートされている、といったところがあります。しかし、繰り返しになりますが、LTSB はそもそも特定用途の固定端末で使うことを想定して作られているエディションである、という点については必ず念頭において考えてください。企業内のユーザのほとんどが LTSB を使って仕事をする、といったシナリオは想定されていません

[バージョンの混在を前提とするインフラへの移行]

さて、ここまで IP/CB/CBB/LTSB について解説してきたわけですが、最後に見落としやすい、けれども非常に重要なポイントを指摘しておきます。それは、 今後の IT インフラは、複数の OS バージョンが混在することになる、という点です。

従来の IT インフラでは、基本的に、すべての社員が完全に同一の OS を利用することを前提としていたケースも多かったと思います。しかしここまで解説してきたように、Windows 10 WaaS 時代では、端末あるいはユーザごとに、複数の更新モデルを使い分ける必要があります。それは結果的に、複数の機能バージョンの Windows OS が混在する環境を生み出します。

image

ですので、例えば RS1 を使っている A さんが隣の B さんの端末を見たらすでに RS2 を使っていたとか、あるいは部屋の隅に置かれている特定業務端末は LTSB で動いている、といったことが発生します。欧米では BYOD も多く、端末ごとに OS バージョンが違うことも珍しくないらしいのですが、日本の場合にはこうした環境にまだ馴染みがないというケースが多いと思いますので注意していただければと思います。

Part 4. Windows 10 導入時の検討の流れ

$
0
0

[導入計画立案の必要性]

ここまで述べてきたように、Windows 10 の導入では WaaS を見越した検討を行う必要がありますが、特に大企業の場合、WaaS における頻繁なアプリ互換性検証の実施が課題になりやすい、という特徴があります。インフラ面では従来の Windows OS のバージョンアップとそれほど大きく変わるわけではないものの、とはいえ保守性の向上(保守コストの削減)や、ネットワーク経由での継続的な FU/SU 配信については課題になるケースも多いでしょう。

こうしたことを考えると、Windows 10 導入では、アプリ/インフラ両側面から対応検討を行うことが望ましいのですが、大企業の場合、インフラを管理する IT 部門と、業務システムを管理している事業部門とが分断されているケースがよくあります。このため、IT 部門と各事業部門とがしっかり連携できる状況を作っておくことも重要です。

image

また Part 5 以降では実際の検討ポイントを解説していきますが、特にレガシーな業務アプリは一朝一夕に改善できないケースが多いため、直近での「とりあえずの」対応と、中長期的な「抜本的な」改善とを分けて考える必要があります。Part 5, 6 を確認していただいたら、下図のような絵を整理していただき、やるべきこと(対応施策)を「インフラ/アプリ」「短期/中長期」に分類していただくことをオススメします。

image

[特に検討を必要とするポイントについて]

特に、企業内の IT インフラを管理している IT 部門の方からすると、「Windows 10 への移行は、従来の OS バージョンアップと何が違うのか? どこの考え方を特に変える必要があるのか?」という点が気になると思いますので、簡単に下図に整理してみました。

image

ざっと要点を解説すると、以下の通りです。

  • インフラ系
    • クライアント設計やハードウェア選定などの作業は従来と全く変わりがありませんが、今後は「よりセキュアな環境」を「低コストで維持し続ける」ことが今まで以上に重要になってきます。このため、① セキュリティに関する検討、② 従来手作業で実施していてかなりコストがかかっているポイントの改善、はぜひ実施すべきです。
    • ②に関しては皆様個々に懸念点が異なると思いますが、多くのお客様で実施されている、パラメータシート管理と手作業でのマスタイメージ作成の 2 点については、この機会にぜひ見直しをオススメしたいです。
    • また、「継続的に進化し続ける IT 基盤」を実現するために、③ FU/SU の配布方法、を検討することも重要になります。この FU/SU の配信方法はアプリ互換性検証と密接な関係を持っており、最終的な結論としては、FU/SU を段階展開(リング配信)する必要があります。
  • アプリ系
    • なんといっても、① WaaS における頻繁なアプリ互換性検証の実施方法、が最大の課題ですが、これをスムーズに行っていくためには、最終的には、② CI/CD やテスト自動化、DevOps などに代表されるようなシステム開発手法の近代化、を進める必要があります。とはいえ、既存レガシーアプリケーションに対して②を推進しろと言われても、そうそう簡単に行えないことがほとんどでしょう。
    • このため実際の対策に関しては、① 塩漬けタイプの業務アプリに対しては可能な範囲でアプリ互換性検証を効率化しつつ、② 新規開発するシステムや再構築するシステムに関しては、開発時に CI/CD やテスト自動化、DevOps などを取り入れながら近代的な開発をする、というアプローチが必要になります。

これらの一連の項目の中で、実は全体を大きく左右しているのが、既存レガシー業務システムのアプリ互換性検証です。互換性検証については旧態依然としたアプローチをしているとコストも莫大になりやすいため、まずはこの部分を抑えてから他の項目について検討していくようにするとよいと思います。このため、本資料では Part 5 でまずアプリ系に関する考え方を、Part 6 でインフラ系に関する改善ポイントを解説していくことにしたいと思います。

Part 5-a. Windows 10 導入時に考え方・やり方を変えるべきポイント –アプリ編①

$
0
0

[大企業におけるレガシー業務アプリ資産について]

ここまで解説してきている通り、Windows 10 への移行における大きな障壁のひとつに、アプリ互換性検証の問題があります。特に大企業では、多数のレガシー業務アプリ資産を抱えていることが多く、従来は数年に一回の OS アップグレード、あるいはサービスパック適用時などに大規模な再テストを手作業で実施していたわけですが、WaaS のように頻度が高まると、同じやり方が通用しなくなります。

image

特に課題になるのは、非近代的なアプローチで開発された、作りっぱなしの既存の業務アプリ資産です。近代的な業務アプリ開発では、テストの自動化や CI/CD、DevOps といった手法を取り込み、リリース後の継続的な保守が容易になるように、常に技術的負債を一定以下に抑え込むような開発・保守スタイルを取ります(詳細は後述)。しかしこうした考え方が一般的ではなかった時に作られたアプリ(いや日本だと今でも、ですけど;)のほとんどは、作った後の保守や互換性検証のことがきちんと考えられておらず、作りっぱなしで後は場当たり的な保守が行われているというのが実態です。こうしたアプリはうっかりすると資産というより負債になっていますが;、既存アプリ負債とか呼ぶと切なすぎるので;、ここではレガシー業務アプリ資産と呼ぶことにしたいと思います。

現在のレガシー業務アプリ資産の大半は、おそらく従来のテクノロジ、すなわち .NET ランタイムや VB6 ランタイムなどで作られていることが多いと思いますので、ランタイム的にも以下のものだけを想定して解説を進めることにしたいと思います。

  • VB6
  • フル .NET Framework (CLR2, 4 系)※ .NET 2.0~4.6 のアプリ
  • IE11
  • ※ 上記のいずれもカーネル互換性のない業務アプリを想定しています

逆に、以下のようなランタイム上で作られているアプリは、今回は対象外として解説します。これらのランタイムはもともと頻繁にバージョンアップを繰り返しており、モダン(近代的)なアプリ開発アプローチを取らないと、そもそも開発・保守ができないためです。

  • エッジブラウザ向け Web アプリ(HTML5, JavaScript, CSS )※ Microsoft Edge, Google Chrome, Mozzila FireFox など向けの Web アプリ
  • UWP (Universal Windows Platform)
  • ASP.NET Core
  • Xamarin

[既存レガシーアプリに対する互換性検証の基本的な考え方]

さて、ここから既存レガシーアプリに対する互換性検証の考え方を解説していきますが、細かい話をする前に、まず基本的なポイントとして、OS バージョン間の互換性は昔に比べて高まっている、という実態を抑えておく必要があります。

image

特にコンピュータ歴の長い方は、ご自宅でのパソコン利用に際して、98 から XP への移行、あるいは XP でのサービスパック適用、さらには XP から 7 への移行などにおいて、アプリの非互換問題が多発した、という記憶をお持ちの方が多いと思います。が、その一方で、7→8、8→8.1、8.1→10 への移行、さらには November Update や Anniversary Update においてはアプリ非互換問題が大幅に低減している、ということを体感されている方が多いのではないかと思います。

実際、カーネル依存性のないデスクトップアプリケーションの場合、Windows 7 以降では高い互換性が実現されています。もちろん、16 ビットアプリ、OS バージョン番号に基づいて制御をかけているアプリ(特にインストーラ)、デバイスドライバに依存するアプリなどのように、クラッシュしたり全く動かなかったりインストールできないアプリなども確かにありますが、カーネル依存性のないデスクトップアプリの場合、少なくとも「まったく動作しなくなる」といった致命的な問題はほとんど出ないと思います。Windows 7 で動作していた .NET 3.5 のアプリのほとんどは、Windows 10 上の .NET 3.5 でもまず十中八九動作するはずで、先行検証しているあるお客様からは、「まるで非互換問題が出てこないんだけれども、名前だけ変えてお金稼ごうとしてない?」とか言われたことがあるぐらいです;。

このようになった最大の理由は、Windows 7 以降、マイクロソフト社内の製品開発において互換性に関するアプローチが大きく変わったことです。簡単に言えば、従来の製品開発では、「とりあえず機能を追加しまくってから、出てきた非互換問題をモグラ叩きのように潰していく」というアプローチであったのに対して、現在の製品開発では、「設計段階から互換性の維持を念頭において開発を進める」というアプローチに変わっています。このため、OS バージョン間の互換性はそれ以前の Windows に比べて遥かに高くなっています。もちろん原理的に非互換問題がゼロになることはないが、かといって必要以上に恐れすぎる必要もない、ということです。

アプリケーションランタイムの観点から見てみると、下図にあるように、レガシー業務アプリの開発でよく利用されるランタイム(VB6, CLR2.0, CLR4.0, IE11)に関しては、細かいバグ修正などを除けば、Windows 7 以降、ほとんど手が入っていません。OS としての機能は継続的に強化されていますが、これらの機能(例えば Cortana など)は UWP などから使われることが多いため、既存のレガシー業務アプリはそのままお使いください、という構図があるわけです。

image

[「現実の」互換性検証で起こる問題点]

……などというオフィシャルな話をすると、「おいおいちょっとマテ、実際に Windows 10 を使うと UI の見た目が変わるじゃないか、これが非互換でなくてなんだというのだ!」と必ず怒られるのが日本の世界です;。例えば同じ .NET アプリを Windows 7, 10 それぞれで動作させてみると、以下のように違いが出てきます。

image

(これに関しては、私自身、米国本社と話しているときに温度感の違いとして強く感じる部分の一つでもあるのですが)、特にミッションクリティカルな業務システムの場合には、「アプリが起動すればよい」というものではありません。確かに上の図を見た場合、「だいたい動作は同じ」ですが、ウィンドウ枠のデザインの違い、UI 部品の見た目の違い、ドット単位の微妙な描画の違い、連動する標準ブラウザなどの細かい違いが出てきます。そしてこれらは、ミッションクリティカルアプリでは、時として業務継続を妨げる問題となり得るものであり、無視できるものとは言えません。

そもそもなぜ .NET ランタイムに変更が入っていないのに UI の見た目が OS によって変わるのか? これは、アプリケーションランタイムが OS の機能を利用しているために他なりません。

image

例えば .NET アプリの場合、計算処理などは .NET ランタイムの中で行われるため、OS をバージョンアップした際に計算結果が変わる、といったリスクはほぼない、と考えてよいでしょう。しかし、UI 描画処理などは OS の API(Win32 API など)を呼び出して行われるため、OS がバージョンアップすれば挙動が変わることがあります。日本のお客様が「OS によるアプリ非互換」を問題にされる場合には、ここを問題にしているケースがほとんどで、これがあるが故に、安易に OS をバージョンアップできない、というケースが多いと思います。

ただ、その一方で現実論として我々が意識すべき点は、非互換問題の発生リスクの大きさです。OS やランタイムに対して加えられる「変更」というのは、その度合いや程度によっていくつかのランクに分けることができます。

  • A. 機能追加や機能変更
    • 例えば Win32 UI 部品の描画ロジックの変更や、既定のブラウザの変更。
  • B. 脆弱性や信頼性に関する修正
    • 例えばセキュリティパッチや、ブルースクリーン発生に対する修正パッチ。

このように分けて考えると、当たり前ですが非互換リスクが大きいのは前者の機能追加や機能変更です。脆弱性や信頼性に対する修正でも、非互換問題が発生しないとは言い切れませんが、そのリスクは前者に比べれば圧倒的に低いです。よって、我々がまずしっかり押さえるべきは A. 機能追加や機能変更、その上でさらに B. 脆弱性や信頼性に関する修正、という具合に、濃淡をつけて考える必要があります。

[アプリケーションのミッションクリティカル度による分類]

アプリの互換性検証を考える際、理屈上は「あらゆる修正に対して互換性を検証する」のが正しいです。このため、大企業の IT 部門の方と話をしていると、半年に一度の大規模再テストは無理だ! と言われてしまうのですが、実態として(よいか悪いかは別として)、実際の開発現場ではそこまできちんとした互換性検証が行われていない場合が少なくありません。例えば、互換性検証の実施レベルを以下の 3 つに分類してみた場合、①または②のレベルでしか互換性検証をしていない、ということが実は非常に多いです。

  • ① 事前の互換性検証をしていない
  • ② サービスパックのような機能追加・変更がある場合のみ、互換性検証をする
  • ③ パッチを含め、あらゆる修正に対して互換性検証を行う

どこまでの互換性検証をすべきか? はアプリケーションのミッションクリティカル度によって異なります。このため、例えば金融系システムや各種基幹系システムでは、③のレベルで互換性検証をしている場合もあるでしょう。しかし、すべてのレガシー業務システムが③のレベルで互換性検証をしていることはまず十中八九ないはずです。これは、③のレベルでの互換性検証が必要な場合には、毎月リリースされるセキュリティパッチに対して毎月テストを繰り返さなければならないためで、このようなテストは事実上手作業では無理(自動化が必須)です。手作業での互換性テストができるのは、現実的には②のレベルまでです。

image

そしてもう一つ、これに併せてご確認いただきたいのが、(Windows 7 以降における)過去のサービスパックやセキュリティパッチの適用において、実際に非互換問題がどの程度発生したか、です。最初に述べたように、Windows 7 以降では OS バージョン間の互換性が高くなっているため、ほとんどのケースでは実態として非互換問題は発生していないはずです。もしそうであるのなら、Windows 10 への移行時に、現在の実施レベルをむやみに高める必要はありません。例えば、現在の実施レベルが②であるのなら、Windows 10 移行後も、②のレベルに留めるべきです。

なぜこのような話が重要になるのかというと、最大の理由は、IT 部門の方が、実際の業務部門側で行われているシステム開発の実態を完全に把握していないことが多いからです。実際にあった話ですが、ある大企業の IT 部門での Windows 10 導入検討において、当初、半年に一度の OS バージョンアップではアプリ互換性検証が間に合わない! ということが大問題になりました。しかし実際の業務部門に確認してみると、Windows 7 の SP1 リリースの際に事前のアプリ互換性検証が行われていなかったこと、またその際に非互換問題が発生していなかった、という事実が判明し、必要以上に心配しすぎだということがわかった、ということがありました。

このアプリ互換性検証は(私もそうだったのですが)つい頭でっかちに考えやすい部分なので、IT 部門の方は、実際にいくかつの業務部門の方にヒヤリングしていただき、現在の互換性検証の実態がどうなっているのかを必ず確認してください

……とはいえ、問題なのは、WaaS の FU(機能アップグレード)において実際にどの程度の機能追加・変更がなされているのか、そしてそれがどの程度、既存レガシー業務アプリに影響を及ぼしうるのか、という点です。もしそれがたくさんあるのであれば、結局は半年に一回、大規模な再テストを繰り返すことになります。これについては、「実際のところどうなのよ?」という議論が必要になるはずです。これについて引き続き見ていくことにします。

Part 5-b. Windows 10 導入時に考え方・やり方を変えるべきポイント –アプリ編②

$
0
0

WaaS におけるアプリ互換性問題を考える上では、Windows 10 → 10 への機能アップグレードにおいて、どの程度、既存レガシー業務アプリに影響を及ぼしうる変更が加えてられているのか、という点が重要になります。もちろん、将来のことはわからない部分もありますが、とはいえ November Update や Anniversary Update の内容を精査してみることは、FU(機能アップグレード)の中身の特性を理解する上で重要になりますので、もう少し細かく見てみることにします。

[Windows 10 FU 間の互換性の実態]

詳細は Part 7 にて解説しますが、Windows 10 の機能アップグレード(FU)で行われた内容については、非公式サイトですが changewindows.org というサイトが(英語ですが)非常によくまとまっており、このサイトを見ると、一覧形式で 8.1 → TH1, TH1 → TH2, TH2 → RS1 で行われた主な機能追加・変更を確認することができます。こちらを確認していただくと、Windows 10 の FU は大まかには以下のような傾向があることがわかります。

  • Windows 8.1 → Windows 10 TH1
    • UI まわりが大きく変更されている。(このため、.NET や VB6 アプリもその影響を受ける)
    • その他、OS 全体に比較的大きな機能追加・変更が加えられている
  • Windows 10 TH1 → TH2 および TH2 → RS1
    • TH1 → TH2 : Edge ブラウザの強化が中心で、他の機能強化はほとんどない
    • TH2 → RS1 : Edge ブラウザに加え、スタートメニューや設定画面、IME などの強化が中心であり、既存レガシーアプリには影響が出にくい変更内容がほとんど

image

changewindows.org サイトを見てみると、各 FU には 100~400 近くの機能追加・変更項目があることがわかりますが、私なりの視点で「レガシーアプリに対して非互換問題を起こしそうな」変更項目をざっとピックアップしてみたのが下のリストです(一部、日本語関係で私が追加した項目も入っています)。もちろん私なりの視点なので、当然、すべての業務アプリでこれだけチェックすれば十分、とはなりませんが、大半の機能追加・変更はスタートメニューなど既存レガシー業務アプリに影響を及ぼさない場所に入っているため、半年に一度の FU のつど、アプリ全体を入念に再テストしなければならない、というわけでもなさそう、という感覚は掴めるかと思います。

  • 8.1 → TH1
    • IME 関連
      • デスクトップアプリのテストボックスに触れるとスクリーンキーボードが表示されるようになった
      • テキスト入力キャンバスが改善された
    • UI デザイン関連
      • ウィンドウ枠のデザインが変更された
      • Win32 コントロールのデザインが変更された
      • タスクバーの既定の色がダークに変更された
      • ウィンドウ枠の色が白に変更された
      • 日本語の既定のフォントが Yu Gothic UI に変更された
    • インストーラ関連
      • Windows OS のバージョン番号が変更された
    • 拡張子関連
      • 既定のアプリの変更方法が変更された
    • ブラウザ
      • 既定のブラウザが IE から Edge に変更された
      • 全画面型 IE が廃止された
  • TH1 → TH2
    • IME 関連
      • テキスト入力パネルが改善された
      • 新しい絵文字が追加された
    • インストーラ関連
      • Windows OS のバージョン番号が変更された
    • 拡張子関連
      • 既定のアプリが “Windows 10 で推奨” と表示されるようになった
    • その他
      • タイムゾーンを自動的に補正する機能が備わった
      • 最後に利用したプリンタが「既定のプリンタ」として扱われるようになった
  • TH2 → RS1
    • IME 関連
      • 日本語 IME が大幅に機能強化された(クラウド候補、入力履歴、候補予測、片手入力キーボード、性能など)
      • 絵文字のイラストが変更された
    • UI デザイン
      • 認証ダイアログのデザインが変更された
      • 游ゴシックが小さい文字でも読みやすいように変更された
    • その他
      • NTFS が 260 文字以上のファイルパスをサポートするようになった

以上を加味すると、実際の Windows 10 への移行に関しては、7/8/8.1 → 10 の移行と、10 → 10 の移行とを分けて考えた方がよい、ということになるでしょう。具体的なやり方は後述しますが、おおまかな考え方は以下の通りです。

  • 7/8/8.1 → 10 : 比較的きっちりと検証作業を行う
    • 日本の業務アプリケーションの多くは UI が非常に複雑なため、若干の見た目の変更であってもエンドユーザからのクレームに繋がり兼ねないため。
    • ただし、以降の 10 → 10 の互換性検証を見据えながら検証作業を進めるのがポイント。
  • 10 → 10 : 極力、効率化・省力化を行う
    • 既存のレガシーアプリに対して影響を及ぼしうる OS への機能追加・変更はごくわずか。
    • このため、この互換性検証は、実施するとしても極力、効率的に実施する必要がある。

image

[アプリ互換性検証の効率化の手法]

では、実際にアプリ互換性検証を効率化するためにはどのような手法があるのか? ですが、代表的なものとしては以下の 3 つがあります。

  • ① テストケースの絞り込み
    • 実施する非互換検証の物理的な実施量を減らす
  • ② 見逃しリスクへの対応
    • テストケースを減らしたことによる見逃しリスクを緩和するための施策を打つ
  • ③ 更なる実施効率化
    • ①②を行った上で、さらに効率化を進めたい場合に追加の施策を打つ

主な具体的施策と併せて示すと、下図のようになります。

image

うちの開発現場ではすでに実施済みだよ、という施策も多いでしょうが、以下に順に、考え方ややり方を示していきます。

[アプリ互換性検証の効率化の手法① テストケースの絞り込み]

まず真っ先にやるべきことは、非互換問題を起こしそうなところに絞ってテストを実施する、というものです。絞り込み方については主に以下の 3 通りがあり、これらを組み合わせて効率的なテストケースを組み立てます。

  • A. アーキテクチャ情報を元に絞り込む(処理の類似性や技術要素などから絞る)
  • B. 業務的観点から絞り込む(重要度の高い業務に絞る)
  • C. 実践的な知見から絞り込む(過去の互換性検証の結果や探索的テストから絞る)

この 3 つの絞り込み方の中でも特に重要なのは A. アーキテクチャ情報を元に絞り込む(処理の類似性や技術要素などから絞る)の考え方です。というのも、実際の非互換問題は下記の図に示したような『技術的接点』で発生しやすいためで、むやみにテストケースを設計するのではなく、こうしたパスを少なくとも 1 度は通すようにテストケースを設計することで、効率的なテストケースを組みやすくなります。
image

上図ではデスクトップアプリの場合について考え方を示しましたが、Web アプリについては注意が必要です。特に既存レガシー業務 Web アプリの場合、Web アプリと言いながら、実際には ActiveX をはじめとするプラグイン技術を多用していることが多く、これらはデスクトップアプリと同様な非互換検証を必要とします。このため、IE11 自体が変わっていなくても、プラグイン部分については、デスクトップアプリ的な考え方でテストケースを組み立てる必要があります。(なお、IE11 は基本的に機能変更が入らない、という指針にはなっていますが、とはいえエンタープライズモードをはじめとする Edge ブラウザとの切り替え機能や、セキュリティ強化に伴う機能変更などは今後も入る可能性があります。このため、こうしたポイントについては念のため確認した方がよいでしょう。)

image

また、テストケースの効率的な絞り込みのためには、濃淡をつけるという考え方が重要です。技術要素に分解して考えた場合、場所ごとに非互換リスクの度合いはずいぶん違います。ですから、例えば下のような表を作成し、領域ごとにどの程度濃淡をつけてテストするのか? ということを意識しながらテストケースを組み立てていくと、費用対効果の高い互換性テストが実施できます。

image

さて、この「非互換情報との突合せによるテストケースの絞り込み」では、以下の 2 つが特に重要になります。

  • どのレベルで非互換情報を確認するのか?
  • どこから非互換情報を入手するのか?

どのレベルで非互換情報を確認するのか?

Part 5-a で述べたように、互換性検証の深さはアプリのミッションクリティカル度に応じて調整する必要があります。例えば、

  • 今まで月例パッチ(毎月のセキュリティパッチ)に対して互換性検証をしてこなかったというアプリの場合は…
    • 今後も、アプリ互換性検証は機能アップグレード(FU)が行われる際に、機能追加・変更に対してのみ行えばよい。
  • 毎月のセキュリティパッチに対しても互換性検証をきちんとしてきたというアプリの場合には…
    • 今後も、毎月のサービシングアップデート(SU)に対して、互換性検証を続けていけばよい。(ただしこの場合は事実上、自動化が必須)

となります。

image

どこから非互換情報を入手するのか?

非互換情報は、公式/非公式様々なサイトから提供されていますが、実のところ、このテストケース絞り込みの目的ですぐさま役に立つものは少ないのが実情です。詳細は Part 7. 「Windows OS の変更ログの入手方法」で解説しますが、例えば『機能アップグレード(FU)が行われる際に、機能追加・変更に対してのみ行う』のであれば、FU リリース時に、機能追加・変更の一覧情報を入手する必要があります。非公式サイトかつ英語という問題点はありますが、最も使いやすいサイトは最初に説明した ChangeWindows.org というサイトだと思います。ちょっと時間を取って実際に見てみていただければと思いますが、

  • ページ上部の「Milestones」を開くと FU の一覧が出てくる。(November Update, Anniversary Update など)
  • そこから FU を選択すると、中間ビルドの一覧(Windows Insider で公開された中間ビルドの一覧)が出てくる。各中間ビルドには、前回の中間ビルドからの差分情報が整理されている。
  • 最後のビルド(緑で示されている)が、最終的に公開されたビルド。これを選択すると、前回の FU からの変更点の積算情報が表示される

例えば、TH1 から TH2(November Update) での変更点はこのページに、TH2 から RS1 (Anniversary Update) での変更点はこのページに情報がありますが、(英語であるという難点を除けば;)使いやすい形で、機能追加・変更に関する変更ログが整理されていることが確認できます。こうしたリストを使って、特に重点的にテストする場所を決めていくとよいでしょう。

なお、SU(累積パッチ)での修正情報については、Windows 10 リリース情報ページが詳しいです。こちらは、今までに提供された SU の一覧と、それぞれに含まれている代表的な修正内容について情報が提供されています。(こちらは KB 情報なので日本語でも情報が提供されています。FU の情報が日本語で提供されてなくて SU の情報が日本語で提供されているというのは正直逆だろう、と思うのですが;

非互換情報を利用する場合の注意点 ~機能追加・変更・修正の境界線問題~

さて、こうした非互換情報を利用してアプリの互換性検証をする場合の注意点ですが、

「どのような非互換情報リストを利用しても、絞り込んだテストだけで非互換問題を事前に100% 洗い出すことは原理的にできない」

ということを理解しておく必要があります。このポイントは敢えて赤字で書くほどむちゃくちゃ重要なことなので、しっかり理解してください

こうした互換性検証のよくある誤解のひとつに、「ベンダーから提供される非互換情報(例えば KB などの情報)を使って検証することで、非互換問題を事前に(完全に)回避することができる」、というものがあります。しかし、この考え方は原理的に誤りです。なぜなら、マイクロソフトに限らず、ベンダーから提供される非互換情報そのものは、すべての変更点を網羅したリストにはそもそもなっていないから(=すべての変更点を網羅したリストを提供していない)であり、同時に、「何を機能追加・変更・修正とみなすのか?」に関しては、ある程度、人間の恣意的な判断が入ることが原理的に避けられないためです。このことを概念的に示したのが下図です。

image

まず、極端な話をしてみると、同一ソースコードをリビルドしても、完全に 1 バイト単位で同じバイナリにはなりませんから、リビルドするだけで、理屈上は挙動が変わる恐れがあります。けれども、我々は多くの場合、同一ソースコードをリビルドした場合には挙動は同じになると考えます。なぜかというと、コンパイラに不具合がない限り、現実的には論理的な挙動は同じになるだろうと考えるからです。あるいは、CPU として Core i7 のマシンを使う場合と、Core i5 のマシンを使う場合とで、理屈上は挙動が変わる恐れがありますが、現実的には業務アプリの挙動は変わらないと考えるのが普通です。このように、「理屈上、原理的に非互換問題が起こりうる」という話と、「現実的に、非互換問題が起こる可能性が高い」という話には、隔たりがあるのが普通です。

このことを Windows OS に当てはめて考えてみると、製品に対するあらゆる修正は、たとえ API 外部仕様が一致していても、挙動が 100% 同じになるとは言えないため、機能変更であるとも言えます。その一方で、あらゆる修正を機能変更であるとみなすと、非互換情報リストが膨大になり、突合せ確認を行うことが不可能になってしまいます。Windows OS の場合だと、細目レベルでは数千~数万件単位の修正が入っているそうなのですが、そんな API レベルの微修正の一覧リストをもらったところで、非互換検証の突合せを行うことは不可能です。このため、実際の業務アプリの非互換検証においては、「実用的なレベル感・粒度感」に整理された非互換情報リストを使うことが、最も重要になります。(項目数ベースでいえば、最大でも 100~500 個ぐらいの粒度に整理されていないと、リストとしては使い物にならないと思います)

実際、現場にいると、お客様から「Windows OS や .NET Framework のバージョンアップに際して、すべての変更点リストを提供してください」と言われることが多いのですが、本当にすべての変更点リストを提供してしまったら、項目数が多すぎて、実際には使い物にはなりません。実際にお客様が期待しているものは、適度な粒度感にまとめられつつ、なおかつそのリストを使うと自分の業務アプリの非互換問題が 100% 事前に洗い出せるという魔法のような非互換情報リストなわけですが、そのようなものを作ることは原理的に考えて不可能です。

結局、過去の経験に基づき、最も実用的なレベル感・粒度感で、最も非互換問題を起こしそうな項目を情報として取りまとめて提供するのがお客様側にとっても最もメリットがあるわけで、マイクロソフトをはじめ、多くのベンダーはそのような取り組みをしています。一方で、こうした情報の取りまとめでは、「何を機能追加・変更・修正とみなすのか?」に関して、ある程度、人間の恣意的な判断が入ることが原理的に避けられません。上図に示したように、機能追加はともかくも、「機能追加」と「修正」の境界線、あるいは「修正」と「微修正」の境界線は、どうしても曖昧になる、ということです。

ですから、非互換情報リストを使って絞り込み再テストを行う方法では、見逃しリスクを大きく低減できるものの、見逃しリスクをゼロにすることは原理的にできません。このため、より高い安全性を実現するためには、実際に見逃しにより不具合が発生した場合のリスクヘッジを考える必要があります。そのための施策が、次に述べる、リング配信と迅速な障害対応です。

Part 5-c. Windows 10 導入時に考え方・やり方を変えるべきポイント –アプリ編③

$
0
0

[アプリ互換性検証の効率化の手法② 見逃しリスクへの対応]

前述したように、非互換情報リスト(変更ログ)を使ってテストケースを絞り込むことで、アプリ互換性検証に関わる工数を大きく削減できますが、その一方で、非互換問題の見逃しによる障害リスクをゼロにすることは原理的にできません。このため、非互換障害が発生した場合の被害を最小化する施策も併せて取る必要があります。具体的な方法は主に以下の 2 つです。

  • A. 一部のアーリアダプタユーザに対する CB(または IP) の先行配信(リング配信)
    • 社員の一部に、先行して CB (または IP)を配布して実業務で使ってもらい、非互換障害が発生した場合には、すみやかに修正を行う。
  • B. ベンダーとの協業強化による迅速な障害修正対応フローの確立
    • 特にアプリを外注している場合には、速やかな修正ができる体制を整えてくおく。

image

それぞれについて補足説明すると、以下の通りです。

A. 一部のアーリアダプタユーザに対する CB(または IP) の先行配信

「アーリアダプタ」とは「早期利用ユーザ」のことで、いきなり全社員に一律で FU/SU を配信するのではなく、アーリアダプタから段階的に FU/SU を配信して利用者を増やしていくことにより、非互換問題発生時の問題を極小化しよう、というものになります。この考え方は Windows OS そのものの開発でも使われているもので、リング配信と呼ばれています。(ここでは話を簡単にするため、FU のリング配信についてのみ解説します。これがわかれば、SU についても似たような考え方ができるはずです。)

FU の実際のリング配信のやり方については、対象となるアプリの種類によって変わります。

  • コンシューマ向けサービスや ISV パッケージ製品・サービスの場合
    • この場合、ユーザ側は FU(November Update や Anniversary Update など)が CB としてリリースされると FU 適用を始めますので、それより先行して利用検証をしなければなりません。
    • このため、このケースでは Insider Preview を使って先行検証・リスクヘッジをすることになります。
  • 社内業務アプリの場合
    • この場合、大多数のユーザ(社員)が FU を使い始めるのは、FU が CBB 扱いになってからになります。
    • このため、一部のアーリーアダプタの社員に対して CB (としてリリースされた FU)を先行配信してしまい、社内業務アプリで不具合が出ないかどうかを確認してもらうことになります。

このように、アプリの種類によって、先行検証の時期や利用するビルドが異なるため、アプリの特性に合わせた、実地検証の期間の取り方などを事前に決定しておくことが必要です。下図はこれを概念的に示したものになりますが、こうした図をまとめておき、誰にどのように配信するのかを決めておくとよいでしょう。

image

ちなみにマイクロソフトの社内においても似たような取り組みが行われており、先行して Windows OS や製品の最新中間版などを積極的に利用する社員を募集する形を採用しています。この仕組みは社内ではエリートプログラムと呼ばれており、新し物好きな社員が積極的に新しい OS を使う形になっています。(私自身も Surface Pro 4 を 2 台使っており、1 台は CB、もう 1 台は Insider Preview Fast Ring で利用しています。)(余談ですが、エリートプログラムは昔はドッグフードと呼ばれていたのですが、ドッグフードはちょっと名前的にイマイチですよねぇ;。犬の餌って;;)

B. ベンダーとの協業強化による迅速な障害修正対応フローの確立

また、併せて見直していただきたいのが、アーリーアダプタやエンドユーザから非互換障害が報告された際に、速やかに修正・再リリースが実施できる体制を整えておくことです。というのも、日本の大企業ではシステム開発を SI ベンダーや協力会社に外注していることが多く、不具合修正のやり取りに大きなオーバヘッドがかかるケースが少なくないためです。

  • 内製あるいはそれに準じる体制を取っていない場合
  • リリースに必要なテスト(デグレードテスト)が手作業で行われている場合
  • 修正作業の依頼や確認に、大量の書類による手続きが必要になっている場合

具体的な改善方法はケースバイケースであまりにも違いすぎると思いますのでここでは触れませんが、このような場合には、やり方の見直しや効率化を図り、速やかな障害修正対応フローを確立するようにしていただければと思います。

image

[アプリ互換性検証の効率化の手法③ テストのさらなる実施効率化]

ここまで、基本的なアプリ互換性検証の効率化として、① テストケースの絞り込み、② 見逃しリスクへの対応、という基本指針を示してきましたが、さらに以下のような手法を組み合わせていくことにより、互換性検証の作業をより効率化していくことができます。

  • A. 仮想環境の活用
    • 以下のような用途で仮想環境を活用すると、テストの作業効率が大幅に改善する
      • 次期 CBB クライアント環境を仮想環境として用意する(互換性検証用のテストマシンの準備工数が大きく削減される)
      • テスト用サーバ側環境を仮想環境として構築する(スナップショットを活用することで、テスト用サーバを容易に初期状態に巻き戻せるようになる)
  • B. 非有識者による再テストの実施
    • 互換性検証のたびに、業務や技術に詳しい SE やエンジニアが狩り出されると、現場業務に支障が出る
      • このため、非有識者でも再テストできるようにする
    • 具体的には、以下のような手法を組み合わせる
      • テスト環境の状態によって、テスト実施結果が変わらないようにテストケースを作成する(リピータブルにテストを設計する)
      • 有識者によるテストを、ビデオ録画などで記録しておき、これを活用する
  • C. UI テストの自動化
    • 仮想環境の利用や、リピータブルなテストケース設計が正しくできていると、UI テストの自動化も可能になる
      • UI テストの完全自動化はコストメリットが薄いが…
      • 互換性検証に必要な最小限のテストケースのみを自動化すると、メリットが非常に大きい
      • 累積パッチに対しても素早い互換性検証が可能になる他、迅速なデグレードテストなども可能になる
    • CUIT (Coded UI Test)ツールや 3rd party 製テストツールなどを用いて、UI 打鍵の自動化や適切な結果検証を行う
  • D. 全社的視点での互換性検証の最適化
    • 特に大企業の場合、全社的な目線で考えると、互換性検証を大きく最適化できる可能性がある
    • 主なポイントとしては…
      • テスト対象とする業務システムそのものの絞り込み(アーキテクチャが似ているシステム群は、そのうち一つだけを互換性検証の対象とする)
      • テストインフラの準備(個々の業務システムごとに用意するより、全社的に用意した方が安価)
      • 互換性検証テストチームの運営(専任チームを作った方が互換性検証のノウハウが貯まる)

こうしたテスト実施改善を進めていくためには、テストインフラの構築とテストツールの導入が欠かせません。というのも、残念ながら日本の開発現場の多くでは、未だ以下のような Excel テストが横行していると思います。

  • Excel 表を使ってテストケース一覧を書き出し、そこに〇×をつけていく。
  • 実施結果の証跡(エビデンス)を取るために、画面スナップショットを Excel の別シートにぺたぺたと貼り付けていく。

しかしこんなことをやっていてはテストの実施効率が全く上がりませんし、過去のテスト結果データを生かして次のテストに結び付けていくこともできません。近代的なテストインフラの構築とテストツールの導入により、テスト実施作業の効率化を推進していくべきでしょう。例えばマイクロソフト製品であれば、Hyper-V を用いた仮想化テスト環境(※ ラボ環境と呼ばれる)を構築したり、テスター向けツールである Microsoft Test Manager (MTM)を使うことでテスト実施作業の効率化を推進できますし、これらのツールや環境を、全社レベルで導入すると費用対効果もさらに高くなります。

image

image

[実際のテスト実施効率改善の難しさ]

ただその一方で、ここで述べた「仮想環境の活用」「非有識者による再テストの実施」「UI テストの自動化」などは、実際の現場で実施されていることは非常に少なく、また「全社視点での非互換検証の最適化」などは、大企業であれば大企業であるほどやりにくいケースが多いのも実際だと思います。

下図は、「開発時にやっておくべきこと」と「スムーズなアプリ互換性検証の実施に必要なこと」の対応関係を示したものですが、この図からわかるように、場当たり的な開発やテストが行われてきた場合、後付けでアプリ互換性検証の効率化を進めることには限界があります。このため、既存レガシーアプリに対しては、① テストケース割愛や、② 見逃しリスクへの対応、を行うのが精いっぱいであり、③ 更なるテストの実施効率化の施策が打てないことがよくあります。このような場合には、敢えて現行システムに対しては施策を打たず、次期大規模改修の際に『テストしやすいように』アプリ開発を行うようにしていくのが現実的でしょう。

image

このように、現実的な既存レガシー業務システムでは、「短期的な対応策」と「中長期的な対応策」とにわけて考えていくことが重要です。今一度、アプリ互換性検証の効率化の大方針を再掲しますが、直近でどこまでやるのか、長期的にはどこを目指すのか、その両方を考えるようにしてください。

Part 5-d. Windows 10 導入時に考え方・やり方を変えるべきポイント –アプリ編④

$
0
0

さて、実際の既存業務アプリを考えると、「理想的なアプリ開発」が行われていないことが多く、「理想的なアプリ互換性検証」を行えない場合が少なくありません。短期的な対策としてはやむなしという部分も大きいですが、中長期的を見据えると、今のままのやり方を繰り返していては苦しくなるだけです。ですからここで今一度、「理想的なアプリ互換性検証」や「理想的なアプリ開発」を再考し、次のシステム開発で何に注意すればよいのか、を考えておくべきです。この観点では、マイクロソフトの社内のやり方に、参考になるヒントが多数存在しますので、少しご紹介してみることにします。

[事例:マイクロソフト社内のアプリ互換性検証作業の効率化]

当たり前のことですが、マイクロソフトの社内にも、皆様の会社と同様、多数の業務システムが存在します。マイクロソフト社内では、MSIT と呼ばれる社内 IT 部門が社内業務アプリを管理しており、経費精算アプリ、物品購買システム、マッサージ予約システム、出退勤管理システム、パフォーマンスレビューシステムなどなど、全世界トータルでは約 2,100 個の社内業務アプリが存在しています。こうした社内業務アプリをどのように Windows 10 WaaS に対応させているのかが、かなり詳細な事例情報として Web 上に公開されています。

image

https://www.microsoft.com/itshowcase/Article/Content/668/Deploying-Windows-10-at-Microsoft-as-an-inplace-upgrade
https://www.microsoft.com/itshowcase/Article/Content/519/Microsoft-IT-improves-LOB-application-testing-ensuring-readiness-for-Windows
https://www.microsoft.com/itshowcase/Article/Content/520/Microsoft-IT-prepares-LOB-apps-for-Windows

マイクロソフト社内の場合、非互換障害が発見された際には必要に応じてアプリ側の修正ではなく Windows OS 側に修正をかける、という点は一般企業と異なりますが、とはいえ非互換検証作業の具体的な効率化手法に関しては、参考になる部分も非常に多いです。なのでぜひ上記の URL をご確認いただきたい……のですが、英文全部を確認するのはさすがに大変だと思いますので、要点を私なりにざっくりと整理すると、以下のようになります。(間違ってたらごめんなさい;、古い情報と新しい情報が混在しており一部情報に食い違いがあったりしているため、完全に 100% 正しくはないかもしれませんが、細かい数字はともかくも要点は間違っていないはずです。)

  • 特に重要なポイント
    • 互換性検証を効率的に進めるため、グローバルで集約テストチームを運営している。
    • 2,100 個の業務アプリから 300 個を選出して集中的にテストしており、さらに段階的な OS 配布を併用することで、致命的な非互換問題の発生を防いでいる。
    • これらにより、互換性維持の費用を $700k/year (約 7,000 万円/年程度)にキープし続けている。
  • 具体的なテスト効率化の方法
    • ① テストケースの絞り込み
      • テスト対象システムの絞り込み
        • テストケースの絞り込みの前に、そもそもテスト対象システム(アプリ)自体を絞り込んでいる。
          • アプリケーションポートフォリオ管理ツールで、社内 LOB システム 約 2,100 個を一元管理。
          • この中から 約 300 個を Key Test Group として選出、これを重点的にテストし、その結果を元にアップグレードを判断。
        • 選出の観点は以下の 2 つ。
          • 業務の重要度 : 約 150 個
          • 技術的類似性や過去の非互換問題に基づく選出 : 約 150 個
        • 選出されなかったシステムは、特に積極的なテストはしない。
          • ただし OS 展開前に、ローカル部門の判断でテストできる猶予期間を 3 週間設けてはいる。
    • ② 見逃しリスクへの対応
      • 段階的な OS 配布
        • 社内アーリアダプタユーザ(ボランティア)が  Insider Program に参加して業務で利用
        • 見つかった障害に対しては R&D と連携し、必要に応じて OS 側を修正して互換性を向上させる
      • コード修正
        • 多くのシステム(約 80%)は内製のため、速やかなコード修正が可能。
        • 非内製アプリは修正に時間がかかるため、なるべくテスト優先度を高めている。
    • ③ テストの更なる実施効率化
      • 集約テストチームの運営
        • Key Test Group に関しては、グローバルで集約テストチームが積極的にテストを実施
      • テストの自動化
        • 自動化テストを駆使し、5 日間で全システムをテストできる状態を保ち続けている
        • 自動化率は 50~85% 程度
      • 仮想環境の活用
        • アプリ互換性検証はすべて仮想マシンで実施
        • ローカル部門のテスト容易化のために、テスト用仮想マシンの貸し出しを実施(※ 現在はクラウドを活用)

この事例でおそらく最も重要なポイントは、アプリ互換性検証の効率化を大きく高めたい場合には、会社全体としての取り組みが必要になる、という点です。これについて次に解説します。

[大企業への WaaS 導入時の注意点]

MSIT の事例では、個々の業務システムの開発チームがずっと個別に保守対応(互換性検証)を続けると限界があるため、会社全体としての最適化を図ることで全体の維持コストを抑える、ということをしています。実際、アプリ互換性検証の効率化を全社視点で考えた場合、会社全体として取り組むと効果が高いものと、個別システムで効率化を進めるべきものにわけることができます

image

しかし日本の大企業の場合、こうした全社最適化のアプローチが困難なことも実際には多いです。その理由は、以下のようなものです。

  • 各業務部門が異なる SIer・ベンダーを使っており、システムが個別最適化されている
    • 技術的アーキテクチャ、作業プロセスがすべて異なるため、全体最適化ができない
  • 予算管理・執行が分断しており、全体最適化が図られていない
    • IT インフラは標準化されているが、開発ツールやテストインフラは標準化されていない
  • IT 部門がインフラ管理に特化しており、アプリ領域の実態に関する知見を持たない
    • IT 部門側がアプリの作りや非互換検証の実態を把握できていない

image

社長目線で考えればこうしたサイロ化はどう考えても悪いわけで、これらの問題が解決されないと、ある一定以上の改善は困難なことが多いです。しかしその一方で、これらは現場の努力だけでは解決できないケースがほとんどであるため、経営層も巻き込んだ検討が必要になります。まずは自分たちだけでできる改善をしつつも、中長期的には全社視点での最適化を進められるよう、経営層に働きかけていくこともぜひ考えてください。

[近代的な業務アプリケーション開発を目指すために]

また、ここまでの話を振り返ってみると、既存レガシー業務アプリの Windows 10 WaaS への対応が困難な理由の多くが、保守フェーズを意識しない作りっぱなしのアプリ開発スタイルに起因していることもわかるはずです。私は新人の頃、先輩社員から「アプリは動けばよいというものではない、保守しやすいようにアプリを作るのがプロの開発者だ」と言われて育ち、今でもやはりこの考え方は大切にしていますし、実際、保守フェーズにかかるコストは開発フェーズにかかるコストよりも大きいと言われるぐらいですから、保守フェーズを意識してアプリを作るのは当然のことでもあります。納期に追われてそれどころじゃないとか、動かすのに手いっぱいで保守のことなんて考えてられないとか、そういう実情があることもわかるものの;、一方で、今のままのやり方を続けることができないのもまた事実です。

Windows 10 WaaS は時代の要求から生まれてきたものであり、業務アプリも、これからは基本的に「進化していく基盤上で動作を保証し続ける」ことが必要になります。このためには、開発手法や開発のガバナンスの考え方の見直しが必要になってきます。

image

では具体的にどのようなポイントを見直せばよいのか? に関して、特に重要な 4 つの点をピックアップしてみます。

  • テクニカルアーキテクチャ領域
  • 画面設計領域
  • テスト領域
  • 技術的負債への対処

テクニカルアーキテクチャ領域

先に述べたように、全社最適化のアプローチが取りづらいのは、各部門がアプリ単位に最適化を行ってしまうことが大きな理由の一つです。ですから、以下のようなポイントを検討することが重要になります。

  • 利用する開発技術と、作り方に関する開発標準の導入
    • 大企業では、各部門ごとに異なる SI ベンダーを利用し、SI ベンダーごとのやり方をそのまま採用しているケースも少なくない
    • 縛りを入れすぎると SI ベンダー側の生産性を損なうが、ある程度の「緩い」開発標準を導入して、『作り』に関する標準化を進めることは、運用や保守の容易化につながる
  • 具体的なポイントとしては…
    • クラウド PaaS モデルの考え方の利用(運用や配置などの流れを標準化できる)
    • 開発技術の「標準」パターンの策定(開発パターンごとに利用する技術を決めておく)

image

画面設計領域

日本で OS 間の非互換問題がここまで大きな問題になりやすい一つの理由は、UI に対する要求が理不尽なほど硬直的である、という点です。ですから、以下のようなポイントを検討することが重要になります。

  • 硬直的な UI デザインの回避(柔軟な UI デザインの採用)
    • 非互換問題のひとつに、日本ならではの職人的精密さを持つ UI デザインがある
    • しかし Windows OS の UI は、時代の UI トレンドに併せて進化してきている
    • 硬直的な UI デザインを行うと、OS バージョンアップ時にレイアウトが崩れやすくなる
  • 具体的なポイントとしては…
    • レスポンシブ対応による、様々なスクリーンサイズへの柔軟な対応
    • XP, 7, 10 などを振り返った際に、すべての OS で通用しうる UI デザインの採用(を考えておけば、硬直的な UI デザインを回避できる)

image

テスト領域

先に解説したように、テストの効率化は「後付け」で考えることが困難です。ですから、以下のようなポイントを検討することが重要になります。

  • 「テスタブル」にアプリケーションを開発する
    • 何も考えずに作ったアプリをテストするよりも、「テストしやすいように」作られたアプリの方が、圧倒的に効率的にテストを行うことができる
  • テスト駆動型(テストファースト)な実装スタイルを確立する
    • 「出来上がってからテスト」するのではなく、「作りながらテストも開発する」
    • 単体機能テストや結合機能テストをコードとして実装し、手作業で行うテストを最小化しておくことにより、繰り返しの互換性検証の実施が容易になる

こうした「先々の非互換検証」や「先々の機能追加」を見越した開発スタイルは、現在の多くの開発現場の開発スタイルと大きく異なるため、現場の実務レベルでの改善が必要になることにも注意してください(結構大変です;)。

image

技術的負債への対処

保守効率を落とす大きな要因の一つは、作りっぱなし・やりっぱなし文化によって発生する「技術的負債の蓄積」です。技術的負債とは、継続的・持続的な開発・テストを行う理想的な状態からの逸脱の度合いのことで、要するに、その場しのぎ・その場限りの作業を繰り返しているとものすごく保守がしにくくなっていく、ということです。

  • 開発中に積極的にテスト自動化を行っていても、ちょっとした緩みでコードが汚くなり、テスト自動化が困難になる
    • 随時更新型アプリやパッケージ製品(SaaS 型製品)などでは、テストの自動化は極めて重要
      • 特に随時更新型アプリでは、きちんとテストが自動化された状態を保つことは死活問題でもある
    • しかし、一時的であっても「実装作業」を優先させてしまうと、テスト自動化は急激に困難になることが多い
      • 開発中は「動くこと」を優先させがち = テストは後回しになりやすい
      • 結果として、設計やコードが汚くなり、テスト自動化がどんどん困難になっていくことがよくある
    • 継続的・持続的な開発・テストのためには、この技術的負債を常に一定以下に抑え込むことが重要になる
  • 主な「技術的負債」の例としては…
    • 行き当たりばったりなアーキテクチャや設計
    • 汚いコード、読みにくいコード
    • 文章化されていない仕様
    • テストコードが書かれていないソースコード
    • ソースコード中の積み残し作業(TODO 項目)
    • 無視されているコンパイラ警告
    • etc…

こうした『とりあえず動作はするが、汚いやりかけ状態の作業』をそのまま放置すると、保守・維持管理や再構築時に大問題となります。実際、業務仕様が不明なため、ストレートコンバージョンであるにもかかわらず再テストがうまくできずに膨大なコストと時間がかかる、なんていうのはよく聞く話です。こうした状況を生み出さないようにするためには、システム開発および保守フェーズにおいて、定期的かつ強制的に「技術的負債を解消する期間(リファクタリング期間)」を取ることが重要になります。

image

なお、ここでいうリファクタリングというのは、コードに限った話ではない、という点に注意してください。設計書、アーキテクチャ、テストなど、システム開発の成果物すべてについてクリーンな状態を保つことが大切です。お客様も我々も、「出来上がったアプリ」だけに目線が行きがちですが、「作ること」だけを是とするのではなく、「高品質に保つこと」も是とする考え方に変えていくことをしないと、結果的に全体コストは下がらない、という点に注意してください。

[まとめ-Windows 10 WaaS での既存レガシー業務アプリ互換性検証の考え方]

ここまで 4 つのパートに分けて、アプリの互換性検証の考え方について解説してきましたが、全体をふりかえって整理すると、以下のようになります。

  • Windows 10 → 10 の既存レガシーアプリ非互換を、過度に恐れる必要はない
    • Windows 10 WaaS に移行した後に、非互換検証を過度に繰り返す必要はない
    • 基本的なアプローチは以下の 3 つだが、既存レガシーアプリでは①②の対応しか取れないことが多い
      • ① テストケース割愛
      • ② リスク低減のための OS 段階展開と迅速な非互換障害修正
      • ③ 更なる互換性検証の実施効率化
    • 現在実施している非互換検証のレベルに基づいて、Win10 移行後の非互換検証方式を決めるとよい
  • 中長期的な対応としては、開発のやり方そのものに対する改善が必要
    • テクニカルアーキテクチャの領域
      • 利用する開発技術と、作り方に関する開発標準の導入により、全体最適化を図る
    • 画面設計の領域
      • 硬直的な UI デザインの回避(柔軟な UI デザインの採用)
    • 非互換検証の領域
      • 「テスタブル」にアプリケーションを開発する
      • テスト駆動型(テストファースト)な実装スタイルを確立する
    • 技術的負債への対処
      • 定期的かつ強制的に「技術的負債を解消する期間(リファクタリング期間)」を取る
      • これにより、アプリケーションを保守しやすい状態に保ち続ける

全体を振り返れば明らかなように、すべての施策を短期的に打つことは不可能ですが、短期的なところだけを見ていればよいわけでもありません。最終的に目指していくべきところ、すなわちシステム開発の近代化(Modernization)を意識しつつ、短期的対応と中長期的対応の両方を考えていくようにしてください。

# なお、私が所属しているコンサルティングサービスでは、(主に大企業様向けに特化していますが)Windows 10 導入支援や、開発近代化支援などのカスタムコンサルティングサービスを提供しています。Windows 10 WaaS 環境への移行や、システム開発を近代化して WaaS 対応はもとよりさらなる高開発生産性を目指したい、といったニーズに関しては、コンサルティングサービスにご相談ください。

Part 6. Windows 10 導入時に考え方・やり方を変えるべきポイント –インフラ編

$
0
0

ここまで Windows 10 導入におけるアプリ互換性検証の話題を扱ってきましたが、一方で、IT インフラに関しても管理方法の見直しが重要になります。現在の日本の大企業では IT インフラ管理に関する「人手での作業」が非常に多く行われていますが、昨今のクラウド化の流れや IT 自動化の流れを踏まえると、こうした「人手での作業」は限界を迎えつつあります。このため、Windows 10 導入に併せて『負の遺産』を清算し、IT インフラの近代化を進めることをぜひ検討してみてください。

見直すべきポイントはいろいろあると思いますが、ここでは特に以下の 3 つのトピックについて取り上げてみたいと思います。

image

[1. セキュリティ]

日本におけるセキュリティ対策の考え方は、どちらかというと「コスト度外視で究極の安全性を追及する」傾向があり、結果的に現場の社員ががんじがらめのやりすぎルールに縛られて苦しんでいるというケースが多いです。ひどいケースになると、セキュリティ関連のルールの抜け道を一生懸命探して仕事を効率化する、なんていうこともありますが、これでは本末転倒です。社員の生産性を高めつつも近代的なセキュリティの脅威に対応していくためには、以下のようなことを考えていく必要があります。

  • 安全性と利便性の両方を取る
    • 安全性を高めようとして制限を増やしすぎると、社員の生産性が低下してしまう
    • 安全性と利便性のバランス(=効用)を考えて、適切な落としどころを考える必要がある
  • 「ルール」を増やさず「技術」を活用する
    • 覚えられない & 守れないルールを作るのではなく、「技術」で安全性を高める

技術的に見た場合には、主に以下の 3 つのカテゴリに分けて考えるとよいと思います。Azure RMS など一部 Windows 10 の新機能ではないものもありますが、既存環境でまだ使われていない場合には、この機会に導入を検討してみてください。

image

なお、これらのセキュリティ機能はすべてを使うということもないでしょうし、また特定端末に対してのみ使う(例えば AD を操作するマシンはセキュアにする必要があるのでそこだけ特に堅牢にする、など)こともあると思います。ただ気を付けておくべき点として、デバイス, OS(アーキテクチャ), ブートシステムなどの選択に影響が出るセキュリティ機能もあります。例えば Windows Hello は生体認証に対応したカメラなどが必要になりますし、Credential Guard や Device Guard であれば、ハードウェアの対応だけでなく UEFI ブートや x64 OS なども必要になります。このように、Windows 10 導入直後には採用しないものの、いずれ採用したいというセキュリティ機能がある場合には、その制約を確認しておくようにしてください。

[2. マスタイメージ管理]

日本の大企業では、Excel による設定パラメータ値の管理と、Word による各種の作業手順書の作成が、今でもかなり行われています。もちろんこれらのやり方は、特に IT スキルのないお客様にぱっと見せて理解してもらう、といった観点で一定の有効性はありますが、その一方で、こうしたシートや手順書の管理が IT 保守コストの増大を招いている側面もあります。できれば以下のような見直しを行って、保守コストの低減を目指した方がよいです。(ちなみにパラメータシートによる管理は日本特有の文化らしいです。まあわかる気がする……;)

  • ① パラメータシートによる設定値の一覧管理
    • 増え続けるパラメータを管理し続けること自体に無理がある
    • 「実態」を実物管理し、そこに機能差分を調べて「追加していく」アプローチを採用する
  • ② 手作業でのクライアント端末マスタイメージの作成・保守作業
    • 端末種類が増えるとイメージ管理作業が膨大になるため、タスクシーケンスを作成する
    • さらに SCCM を使えば、キッティング作業も大幅に簡素化される

image

[3. FU/SU 配信制御]

Windows 10 の導入とは、最新化され続ける IT インフラへの移行であり、そのためには FU/SU をオンラインで配布し続けられる環境を整えることが重要です。しかし、ネットワークを使って OS を更新する、という仕組みはまだまだ日本の大企業では馴染みが薄い(現在は端末入れ替えに併せて OS を更新するのが一般的)ため、FU/SU の配信制御方式について検討しておく必要があります。中でも特に重要なのは以下の 2 点です。

  • ① リング配信制御
    • アプリ非互換の見逃しリスク低減のため、FU/SU の段階展開が必要になる
    • 拠点のような単位ではなく、部門内・拠点内での端末単位の段階展開が必要であることに注意する
  • ② 分散拠点への FU/SU 配信
    • FU は 3~4GB(半年ごと)程度、SU は(累積パッチであるため)最大 1GB 程度(月次)
    • このため、細い WAN 回線を利用している分散拠点については、配信方法の検討が必要

image

①については特に注意が必要なので気をつけてほしいのですが、FU/SU を先行配信するのはアーリアダプタユーザ、すなわち『特定のユーザ』に対してです。例えば②の図において、各拠点単位に順次 FU/SU を配信していくのではなく、各拠点の中の特定端末から順次配信していく、という形が求められます。また逆に、デバイスドライバの不具合などでアップグレードを抑止しなければならないようなケースでは、特定のハードウェアを使っているユーザに対しては配信を一時的に差し止める、という動きをさせなければなりません。

FU/SU の代表的な配信方法は主に以下の 3 通り(+メディア配布)です。

配信方法 リング制御 分散配布 P2P オプション 備考
SCCM ◎ 柔軟に可能 ◎ 配布ポイント ◎ BranchCache •SCCM のバージョンアップが必要

•SCCM は端末管理やマスタ管理にも活用可能

WSUS △ 可能だが面倒 ◎ 子サーバ ◎ BranchCache
◎ DO (RS1 以降)
•差分配布(高速インストールファイル)をサポート

•O365 ProPlus の配信ができない

WU × 細かい制御は困難 × なし ◎ DO (WUDO)
メディア (手作業での配布) (手作業での配布) (手作業での配布)

※ 略称は以下の通りです。製品などの概要は Web サイトに情報が多々ありますので、検索してみてください。

  • SCCM : System Center Configuration Manager
  • WSUS : Windows Server Update Services
  • DO : Delivery Optimization
  • WU : Windows Update
  • WUDO : Windows Update Delivery Optimization

お客様の環境に応じたケースバイケースの検討が必要なため、一概にどれを選べばよいとは言えないのですが、まずは以下のような指針で考え始めてみるとよいと思います。

  • ① リング配信制御
    • 『特定のユーザ』や『特定の端末』への段階展開が必要だが、この制御は SCCM が最も柔軟にできる
    • SCCM はマスタイメージの作成や端末管理などでも活用できるため、SCCM が導入されている場合には、これが第一選択となる
    • SCCM が導入されておらず、かつ導入予定がなければ、WSUS, WU のどちらかを選ぶ(が、制限があったりリング配信制御が大変だったりするので要注意)
  • ② 分散拠点への FU/SU 配信
    • WAN 帯域が細い場合、分散拠点側に配布ポイントや子サーバを配置する方法が考えられるが、分散拠点側でこうしたサーバマシンを運用できないケースもある
    • このような場合には、P2P オプションを使う
    • WAN 帯域が極端に細い場合には、最後はメディア配布でカバーする

実際のところ、どの配信方式が最適なのかは机上検討だけでは決めきれない部分があります(特に帯域については机上検討だけでは困難)。このため、ある程度は「やってみて考える」という部分が出てくるかと思います。また、回線コストも下がっている傾向はありますので、中長期的には WAN 回線の増強なども視野に入れて検討を進めていただければと思います。

[継続的な情報入手について]

さて、Windows 10 の WaaS では、機能追加は例えばスタートメニューや設定画面、Cortana などの OS そのものに入ってきます。ということは、IT インフラ管理者は、Windows 10 WaaS で提供される新しい機能をどのように活用するのかを積極的に考えていく必要がある、ということになります。この検討に関しては、レガシー業務アプリの互換性検証に利用する変更点一覧情報のような細かいリストを元に考えるよりも、まず「セキュリティ」や「管理機能」などの大きな粒度で確認していった方がやりやすいと思います。この目的のためには、Windows for Business サイトを活用すると便利です。

image

この 2 つのマイクロソフト公式サイトは、「大きな粒度での」機能追加・変更を把握するのに便利です。まずはこちらで概要を把握し、その後、新機能を詳細検討し、グループポリシーやマスタイメージへの組み込みなどを進めていくとよいでしょう。


Part 7. Windows OS の変更ログの入手方法

$
0
0

ここまでアプリ・インフラ両側面から Windows 10 WaaS 環境への移行についての注意点などを解説してきましたが、最終的にはインフラ/アプリどちらの観点であっても、FU/SU での変更点を把握して検討することが重要になります。

  • インフラ観点 : FU で提供される新機能をどのように取り込むか?
  • アプリ観点 : FU/SU で行われた変更でどのようなデグレードが発生しそうか?

この Windows OS の変更ログは様々なチャネルを介して提供されており、自分の目的に合ったものを探して利用する必要があります。

[FU/SU 配信の流れと IP/CB/CBB の関係の再整理]

まず復習として、FU/SU の配信タイミングと、IP/CB/CBB の関係を整理すると、以下のようになります。

image

  • 用語の整理
    • IP : Insider Preview (アーリアダプタ向けに公開される中間ビルド)
    • CB : Current Branch (コンシューマ向けのリリース)
    • CBB : Current Branch for Business (ビジネスユーザ向けのリリース)
    • FU : Feature Upgrade (いわゆる従来のサービスパックに相当)
    • SU : Servicing Update  (いわゆる従来の累積セキュリティパッチに相当)(※ 今後は QU : Quality Update に名称変更の予定)
  • 基本的な流れは、以下のように整理することができます。
    • ① IP リリースごとに、機能追加・変更と、修正(脆弱性・バグ)が行われる
    • ② ①がある程度貯まると、CB として FU がリリースされる
    • ③ FU リリース後は、サポート終了まで修正(脆弱性・バグ)が SU として配信される

この一連の流れにおいて、我々は以下を整理しておく必要があります。

  • 公式/非公式サイトから、どのタイミングでどのような情報が提供されているのか?
  • 自社での検討のために、どのタイミングでどのような情報が欲しいのか?

重要なので Part 5-b で解説したことを繰り返しますが、変更情報には、「機能追加・変更」に関する情報と、「修正」に関する情報があり、どのレベルで非互換情報を確認するのかを意識することが非常に大切です。

image

簡単に言えば、以下のようになります。

  • ミッションクリティカルが高い場合には…
    • 機能追加・変更だけでなく、修正情報(パッチ情報)についても確認することになるが…
    • この場合、パッチリリースごとの確認が必要 → 自動化されたテストの繰り返しが必要です。
  • ミッションクリティカル度が中程度の場合には..
    • この場合には、機能追加・変更を中心に確認することになります。
    • よって、FU のリリースのつど(=CB がリリースされるつど)、機能追加・変更を確認することになります。

ではおさらいはこの程度にして、実際にどのような場所から機能追加・変更・修正に関する情報を入手できるのかを整理します。

[代表的な変更ログ情報の提供サイトについて]

公式/非公式含め、代表的な変更ログ情報サイトには以下のようなものがあります。

サイト名 スコープ 提供頻度 情報粒度 公式/非公式 言語 注意事項
Windows 10 ロードマップ OS 全体 FU 概要レベル 公式 日本語あり 全体像を掴むのに便利
Windows Insider blog OS 全体 IP 機能追加・変更・修正 公式 英語のみ 情報が細かいため整理が必要
Microsoft Edge Changelog Edge のみ IP/FU 機能追加・変更・修正 公式 英語のみ Edge に特化
What’s new in Windows for developers 主に UWP FU 機能追加・変更 公式 英語のみ ほぼ UWP に特化
Windows blog for Japan OS 全体 主に FU 機能追加・変更 公式 日本語のみ 重要なトピックに絞って解説
ChangeWindows.org OS 全体 IP/FU 機能追加・変更 非公式 英語のみ 非公式だが極めて便利
ASKVG OS 全体 IP/FU 機能追加・変更 非公式 英語のみ  
Windows 10 リリース情報 OS 全体 SU 修正 公式 日本語あり いわゆる KB 情報
Windows and Windows Server compatibility cookbook OS 全体 機能追加・変更 公式 英語のみ Windows 7~8.1 までの情報

多分に私見が入りまくりますが;、それぞれのサイトを紹介すると、以下のようになります。

Windows 10 ロードマップ

  • https://www.microsoft.com/ja-jp/WindowsForBusiness/windows-roadmap
  • よいところ
    • 概要レベルで、機能追加・変更の全体像を掴むことができる
    • ユーザの用途(大企業向け/中小企業向けなど)によって情報が整理されている
    • 今後のロードマップも(ある程度)知ることができる
  • 残念なところ
    • 細かい内容はほとんどわからないため、他の資料を確認する必要がある
  • image

Windows Insider blog

  • https://blogs.windows.com/windowsexperience/tag/windows-10-insider-preview/
  • よいところ
    • マイクロソフトから公式情報として公開されている、機能追加・変更・修正情報
    • 機能追加・変更をほぼ網羅しており、修正情報は代表的なものが取り上げられている
  • 残念なところ
    • プレビュービルド毎に情報公開しているが、機能アップグレード毎に整理されていない
    • このため、アプリ互換性検証で利用しようとすると、FU 間でのすべての IP の情報を自力でまとめ上げなければならない
  • image

Microsoft Edge Changelog

  • https://developer.microsoft.com/en-us/microsoft-edge/platform/changelog/
  • よいところ
    • Edge 開発チームから公式情報として公開されている機能追加・変更・修正情報
    • 機能追加・変更を網羅しており、修正情報は代表的なものが取り上げられている
    • Web 上で、任意のプレビュービルド間や CB 間での差分情報をまとめて表示することが可能(← この機能がよくできています)
  • 残念なところ
    • (当たり前ですが)ブラウザ関連の情報しかないこと
  • image

What’s new in Windows for developers

  • https://msdn.microsoft.com/en-us/windows/uwp/whats-new/windows-10-version-1607
  • よいところ
    • マイクロソフトから公開されている開発者向けの機能追加・変更の情報
    • 主に UWP 開発における FU 間でのアプリ開発者向けの機能追加・変更点が整理されている
  • 残念なところ
    • UWP 関連(アプリ開発ランタイム)の情報しか含まれていないこと
    • OS 側の変更点についてはほとんど含まれていないこと
  • image

Windows blog for Japan

  • https://blogs.windows.com/japan/
  • よいところ
    • 日本固有の情報について提供している公式の Web サイト
    • 日本語で重要な情報が提供される
  • 残念なところ
    • 重要な変更情報の提供にとどまっており、OS の変更情報が網羅されているわけではないこと
  • image

ChangeWindows.org

  • http://changewindows.org/
  • よいところ
    • 公式サイトなどの情報を元に作られた、非公式の機能追加・変更情報サイト
    • Insider Preview 単位の情報提供に加え、機能アップグレードごとに集約情報を提供
    • PC, Mobile, Xbox, Server, IoT すべての情報をわかりやすく一覧化して情報提供
  • 残念なところ
    • 公式サイトではないこと(というかこれが非公式サイトというのが未だ信じられないのですが;;)

ASKVG

  • http://www.askvg.com/category/windows-10/
  • よいところ
    • 公式サイトなどの情報を元に作られた、非公式の機能追加・変更情報サイト
    • Insider Preview 単位の情報提供に加え、機能アップグレードごとに集約情報を提供
  • 残念なところ
    • ChangeWindows.org に比べて説明は細かいが、一覧性には劣る

Windows 10 リリース情報

  • https://technet.microsoft.com/en-us/windows/release-info.aspx
  • よいところ
    • サービスアップデート(累積パッチ)の内容を解説した内容
    • CB リリース後の脆弱性・信頼性・バグ修正に関する情報を提供するもの
    • 日本語での情報提供もある
  • 残念なところ
    • サービスアップデートの情報しかない=機能アップグレードの情報がない
    • このため、実態としては使いどころがほとんどない
  • image

また、Windows 10 WaaS とは少し異なるのですが、こちらの情報についても紹介しておきます。

Windows and Windows Server compatibility cookbook

  • https://msdn.microsoft.com/en-us/library/windows/desktop/hh848074.aspx
    (日本語版はこちら、ただし Windows 10 部分のみ) 
  • よいところ
    • Windows 7→8, 8→8.1 における主な機能追加・変更に関する情報が取りまとめられている
    • 非互換項目についてはワークアラウンド(回避策)が掲載されているものもある
    • 下図にあるように、実際の Windows 10 への移行においては、① 初回の Windows 10 への移行と、② 移行した後の WaaS による継続的な更新とがある。②に関してはここまでに取り上げてきた情報が役立つが、①の部分に関してはこの Cookbook の情報が役立つ。
    • image
    • ただし、7/8/8.1 からの移行の場合、UI コントロールの見た目の変更に伴い、比較的大規模なアプリ互換性検証を行う場合が多いと考えられるため、この情報を使う必要があるかどうかに関してはケースバイケース
  • 残念なところ
    • 機能追加・変更がきちんとまとまっているのは 8.1 までで、10 以降に関しては情報がほとんどない
    • 英語の情報しかない

[Premier WaaS 非互換情報提供サービスについて]

ここまで代表的な情報提供サイトをご紹介してきたのですが、中程度のミッションクリティカル度を持つアプリ互換性検証を想定した場合、実際にほしいのは以下のような情報です。

  • FU ごとに、機能追加・変更に関する情報が一覧として整理されている情報がベンダーから欲しい。
  • 突合せ確認が簡単にできるように、表形式などでわかりやすく一覧化されていると使いやすい。できれば日本語で。

この要件を考えた場合、上記の書いたサイトはどれも一長一短で、どうしてもある程度自分でのとりまとめが必要になります。

  • Windows 10 ロードマップ情報 : アプリ互換性検証の目的で使うには情報が粗すぎる。
  • Windows Insider blog : 情報としてはよいが、Insider Preview 単位に情報が細かく分割されてしまっているため、整理する手間がかかる。
  • Edge Changelog : 情報をまとめて一括表示できるのは便利だが、ブラウザの情報しかない。
  • What’s new in Windows for developers : FU 間の差分情報が一覧で整理されているのは便利だが、UWP 系ランタイムの情報しかない。
  • Windows blog for Japan : 情報の網羅性がない。
  • ChangeWindows.org : 内容・まとめ方ともにほぼベストだが、英語かつ非公式。
  • ASKVG : 一覧形式にまとまっていないため、見るのが大変。
  • Windows 10 リリース情報 : そもそも SU の修正情報しか掲載されていない。
  • Windows and Windows Server compatibility cookbook : Windows 10 以降については情報がほとんど含まれていない。

私が一通り使ってみた感触では、ChangeWindows.org の情報が一番使い勝手がよく、次点がその情報ソースである Windows Insider blog かな、という印象があるのですが、いずれも上記に述べた課題があります。正直なところ、実用上は ChangeWindows.org + Windows blog for Japan の 2 つの情報でいけると思うのですが、大企業の方(特に IT 部門の方)だとなかなかこれでは納得できない & 各業務部門の開発現場を納得させられない、ということがあるかもしれません。

こうした事情から、私の方で Premier サポートの方々と相談し、(有償サポートではあるのですが)日本の Premier サポートサービスの提供サービスメニューの一つとして、「WaaS 非互換情報提供サービス」「Windows 7 → 10 非互換情報提供サービス」という名称で、機能追加・変更の一覧情報を FU リリースのつど提供できるように調整しました。

image

以下のような項目を一覧化して取りまとめています。ざーっと機能追加・変更を眺めて、各自の業務アプリに影響しそうな項目を抽出するのに役立てることができると思います。弊社プレミアサポート契約をお持ちであれば、有償ですがそこから入手が可能ですので、どうしても非公式情報や英語の情報では……といったことがあれば、弊社プレミアサポートまでお問合せください。

  • カテゴリ
  • 変更内容
  • 変更が加えられたバージョン/ビルド番号
  • 変更種別(機能追加・変更・削除)
  • 参考 URL(情報ソースとなっているより詳しい URL)
  • 要注意事項(レガシー業務アプリに非互換問題を引き起こしやすそうな項目)
  • 備考

またここでは特に取り上げていませんが、私が所属しているコンサルティングサービスでは、(大企業様向けに特化していますが)Windows 10 導入支援や、開発近代化支援などのカスタムコンサルティングサービスを提供しています。Windows 10 WaaS 環境への移行や、システム開発を近代化して WaaS 対応はもとよりさらなる高開発生産性を目指したい、といったニーズに関しては、コンサルティングサービスにご相談ください。

[Windows OS の変更ログの利用上の注意点について]

最後に、Part 5-b. で触れたことの繰り返しになりますが、どのような非互換情報リストを利用しても、絞り込んだテストだけで非互換問題を事前に100% 洗い出すことは原理的にできない、という点についてはくれぐれも注意してください。マイクロソフトに限らず、ベンダーから提供される非互換情報そのものは、すべての変更点を網羅したリストにはそもそもなっておらず(=すべての変更点を網羅したリストを提供していない)、また同時に、「何を機能追加・変更・修正とみなすのか?」に関しては、ある程度、人間の恣意的な判断が入ることが原理的に避けられません

image

ですから、以下の 2 点については必ず意識するようにしてください。

  • どのような変更ログを利用するとしても、絞り込み再テストを行う方法では原理的に見逃しリスクをゼロにすることはできない。
  • 故にリスクヘッジのためには FU/SU のリング配信と迅速な障害対応を組み合わせることが必須である。

[まとめ~エンタープライズ企業における Windows 10 導入と WaaS 適用の要点]

というわけでかなりの大ボリュームでお送りした一連の blog エントリですが、締め括りとして要点をまとめておきます。

  • エンタープライズ企業における Windows 10 の導入では、その後の WaaS 適用を見越した対応を行うことが重要である
    • Windows 10 の導入 = 継続的にアップグレードされ続ける環境への移行である。
    • このため、WaaS での継続的な機能アップグレードの仕組みを理解するとともに、過去のやり方を見直し、効率的な保守・運用ができる仕組みを整えることが重要になる。
  • 代表的な Windows 10 導入の目的
    • 1. セキュリティ強化、2. モバイルワークスタイル、3. 多彩なデバイス活用、などを検討してみる
  • WaaS の正しい理解
    • IP/CB/CBB/LTSB の使い分けが重要、特に LTSB は一般の OA 端末で利用するものではないことに注意する
    • Windows 10 の更新には、FU、SU、ストアアプリの更新がある
    • FU は 1 バージョンスキップするものではなく、順次アップグレードしていくべきものであることに注意する
  • Windows 10 導入時の検討の流れ
    • Windows 10 の導入時にやるべきことは、アプリ/インフラ、すぐに対応できること/できないことに分けて考える
  • Windows 10 導入時に考え方・やり方を変えるべきポイント – アプリ編
    • ミッションクリティカル度に応じて、互換性検証のレベルを変える
    • アプリ互換性検証の効率化の基本指針は、① テストケースの絞り込みと、② FU/SU のリング配信によるリスクヘッジ。
    • さらに効率化を進めたい場合、全体最適化が必要なこともある。その際は、中長期的な計画として、システム開発の近代化に取り組んでいく。
  • Windows 10 導入時に考え方・やり方を変えるべきポイント – インフラ編
    • 特に、セキュリティ、マスタイメージ管理、FU/SU 配信方式について検討するとよい。
  • Windows OS の変更ログの入手方法
    • いつどのような目的でどんな非互換情報(変更ログ)が必要になるのかを考え、それに合わせた情報入手方法を決定する。
    • 公式/非公式の様々な Web サイトがあるため、自分の目的にあった情報入手方法を検討しておく。

Windows 10 の導入は、新しい近代的な IT 環境への移行である、と考え、本 blog エントリを参考に、ぜひ IT 環境やシステム開発の近代化を進めていただければと思います。長文にもかかわらず、最後までお付き合いいただいてありがとうございました。

拝啓『変わらない開発現場』を嘆く皆様へ ~変わっていくエンタープライズ系業務システム開発とマイクロソフトエンタープライズサービスの取り組み~

$
0
0

image

 昨年、今年と 2 回に渡って de:code にてエンプラ系 SIer さんの PL, PM, SE を対象としたセッションを担当しました。エンプラ系 SI の『闇』はかなり深いものがあり、現場担当の方々はそれを改善すべく日々奮闘されていると思うのですが、その一方で、全体論としての捉え方が正しくないが故に、アプローチが誤っていたり掛け声だけで終わってしまっているケースも少なくありません。例えばエンプラ系開発現場でも最近はトップダウンで DevOps に取り組め、なんていう指示が出たりすることもあるのですが、実際にそれがうまくいっているお客様をなかなか見かけないのも事実です。

 こうした背景があり、de:code 2017 ではセッションを担当すると同時に、参加者全員に配布するマーケティングのリーフレットを使って、筆者赤間の所属するマイクロソフト エンタープライズサービス(有償サービス部門)での最近のコンサルティングの取り組みをいくつかご紹介するという試みをしてみました。マーケティング部門の費用を使っている関係で、弊社サービス部門の営業色が入った内容にはなってしまっているのですが、その一方で、この情報は(特にエンプラ系 SIer やエンド IT 部門、IT 子会社のマネジメント層の方々にとって)組織やチームをどういった方向に導いていけばよいのかの指針になる、とも考えています。実際、いくつかのお客様でこの取り組みを私からご紹介したのですが、自組織の今後の方向性を整理する上で非常に参考になった、という F/B が多かったです。

 個々の皆様の事情はそれぞれ違えど、多少でも参考になる部分があればと思い、blog 化してこちらのサイトにも掲載することにしました(基本的な骨子はリーフレットと同じですが、ページ数の関係で書ききれなかったことを加筆修正しています)。よろしければぜひご覧いただければ幸いです。


■ エンプラ系 業務システム開発を俯瞰する

 クラウドに代表される昨今の技術革新は、業務システム開発の在り方に大きな影響を与えてきました。インフラ、アプリ、運用などの領域に分けてみると、以下のような変化が起きています。

image

 これらの中でも特に重要なのが、「アプリ特性に応じた開発のやり方をする」という考え方です。様々な分類方法が提唱されているのですが、ここではシンプルでわかりやすい、SoE(System of Engagement / お客様との絆を強めるためのシステム)SoR (System of Record / 事実を記録していくためのシステム)という分類を取り上げてみます。SoE 型システムとは「コンシューマ向け、フロントエンド、オープン、B2C」といった特性を持つシステムで、代表例としてはコンシューマ向け Web サイトやゲームアプリ。一方、SoR 型システムとは「エンタープライズ系、バックエンド、基幹系、B2B/B2E」といった作成を持つシステムで、代表例としては金融系勘定システムのようなものが挙げられます。

image

 並べてみると明らかなように、これらはシステム開発としての基本的な特性が大きく異なります。例えば SoE 型システムはページ数や業務数は少なめですが、かわりに極めて先進的な技術が投入されます。一方、SoR 型システムは、一つ一つのロジックはさほど難しくないものの膨大な数の業務が存在し、品質の安定性重視のために枯れた技術が好まれる傾向にあります。

 また、開発特性が異なれば、求められる開発文化も当然異なります。例えば SoE 型システムでは「素早くリリースして素早く軌道修正していく」という Try & Error の文化が重視されますし、SoR 型システムでは「大規模システムを早く・安く・上手く作るためにいかにプロジェクトやメンバーを制御するのか」といった点が重視されます。

 もちろん実際の個々のシステムはここまで両極端ではないでしょうが、この分類に従って考えてみると、いろいろ考えやすくなるのも事実です。例えば日本のシステム開発市場に関して考えてみると、

  • いわゆるエンプラ系 SIer での業務システム開発は SoR 型システムが圧倒的多数。(この傾向は今後もおそらく変わらない)
  • アジャイルやDevOps プラクティスは SoE 型システム開発界隈から生まれてきた手法であり、エンプラ系 SIer の SoR 型システム開発には単純適用できないことが多い。
  • 最近ではお客様が新規性の高いビジネスを模索していることも多く、エンプラ系 SIer といえど SoE 型システムの開発に取り組まねばならないことが多くなってきている。

といった実情があるのですが、こうした状況が整理されないまま、DevOps やアジャイルは日本に定着するのか? 的な議論が数多くなされています。こうしたことが、日本の開発現場の改善に向けた建設的な議論が進みにくいことの一因ではないかと私は考えています。

 また加えて言うのであれば、私は日本と欧米とでは随分と状況が異なると考えています。

  • 例えば、「欧米では Scrum が基本で DevOps が当たり前」と言われるが、そもそも欧米で 2000 年代にオフショア化が非常に進んだことを考えると、欧米の国内に残った「高付加価値 SI」は、必然的に内製でも成立するような(内製でないと成立しないような) SoE 型のような先進性を求めたものになる。よって、そこでアジャイルや DevOps などのプラクティスが進化・発展するのはある意味当たり前。
  • その一方、日本は世界中でも言語障壁が特に高い国であり、欧米のように大規模 SoR 型システム開発をうまくオフショアできていないのが実態。結果として、国内には Web サービス系企業やゲーム業界のように積極的に DevOps プラクティスを活用する企業と、確実性をなにより重視し、膨大なドキュメントに基づく大規模な SoR 型システム開発を実施するエンプラ系 SIer 企業とがごった煮で共存している形となっている。

 よく、「システム開発に関して日本はガラパゴスだ」と言われるのですが、むしろこれは逆で、実際には欧米(特にアメリカ)のシステム開発の方がある意味よっぽどガラパゴスなのではないかと思うのです。というのも、アメリカにおけるオフショア先であるインドや中国などにおいて大規模開発が行われる場合は当然受託開発であり、そこではやはりウォーターフォール的な開発文化が強いらしく(私の見聞きしている範囲なのでバイアスはあるかもしれませんが)、その部分に関しては、日本の SIer とあまり状況は変わらないように見えるのです。……と考えると、おそらく日本のシステム開発の市場というのは、簡単に言えば「世界の縮図」ではないかと思うのです。

 しかしここで勘違いして欲しくないのは、仮に欧米のシステム開発がガラパゴス化していたとしても、そこからエンプラ系 SIer の SoR 型システム開発が学ぶべきこと・学べることはたくさんある、という点です。「ガラパゴス化」というのはあまりいい意味で使われない言葉ですが、最先端を突っ走ることによって多数の様々なプラクティス(実践的手法)が生み出されていることも事実であり、こうした内容は開発文化の違いを意識ししつつ(=きちんと取捨選択しながら)積極的に取り入れていく必要があります。

image

 こうした背景を元にすると、私を含めた、エンプラ系 SI 界隈に携わる人々が取り組まなければならない課題は主に以下の 2 つであると考えています。

  • ① SoR 型システム開発における、更なる開発生産性の改善
  • ② 先進的ビジネスのための SoE 型システム開発への取り組み

 以降では、これらに関する私の考えと、マイクロソフト エンタープライズサービスによるコンサルティングの取り組みや事例をご紹介していきます。


■ ① SoR 型システム開発における開発生産性の改善 ~開発近代化コンサルティングサービス~

 開発技術の進化があれば、その恩恵により開発生産性は改善されるはずです。……にもかかわらず、実際のエンプラ系システム開発現場ではその進化の恩恵を受けられていないことが多いのではないでしょうか? これには様々な理由があると思いますが、中でも大きな理由のひとつとして、日本の SI 商慣習(多重請負構造)における『上流』の仕事のやり方が、10 年以上も変化していない(ような現場が多い)ことが挙げられると私は考えています。

image

 当たり前のことですが、モノづくりのやり方が変われば、設計手法やプロマネ手法もそれに合わせた見直しが必要になります。この点は、私が昨年・今年と de:code で一貫して取り上げてきたテーマで、実践論として具体的なレベルまで踏み込んで解説しています(仔細は以下の ppt やビデオをご覧ください)。

  • de:code 2016 CLT-016 拝啓『変わらない開発現場』を嘆く皆様へ ~エンプラ系 SI 開発現場の「今」を変えていくために~
    https://channel9.msdn.com/Events/de-code/2016/CLT-016
    https://docs.com/decode2016/9106
  • de:code 2017 DO-08 『変わらない開発現場』を変えていくために ~エンプラ系レガシー SIer のための DevOps 再入門~
    (ppt, ビデオは後日公開予定)

 しかし上記のセッションで説明されているような「ベストプラクティス」がすでに存在するにもかかわらず、 Excel 設計書に代表されるように、日本の開発現場は昔ながらのやり方を淡々と続けていることが少なくありません。実際の開発現場を数多く訪問している私の肌感覚ですが、その最大の理由は、IT 部門や SI 関連会社、あるいは SIer のプロパーが、最新の開発技術やモノづくりのやり方を単に「知らない」、そしてその結果、それに併せた最適な SI のやり方を考えられなかったり、改善の打ち手を考えられなかったりすることが多いためだと考えています。

 これは、SIer のプロパーが怠けている、ということではありません。いやむしろ、SIer のプロパーさんほど身を粉にして働いている方々はいないでしょう。にもかかわらずなぜこのような状況が発生するのか? その原因の一つに、私は IT 部門や SIer における過度な外注やアウトソーシングがあると考えています。

image

 上図に示したのはシステム開発の業務遂行に必要な「5 つの専門性」です(仔細はこちらのエントリを参照)。こうした役割を、SIer のプロパーさんと、協力会社の方々によって分担していくわけですが、このアウトソースの仕方に問題がある場合が少なくありません。通常、SIer のコアコンピタンス は「プロマネ」「業務」「技術」の 3 つですが、 行き過ぎたアウトソースや協力会社への依存により、 社内にアーキテクトがいなくなっているケースが非常に多いです。その結果、現場あるあるとして以下のようなことが起こります。……というよりホントにこんな現場ばっかです;(涙)。

  • プロマネや業務 SE に対して 技術的視点から下支えする人材がおらず、技術的に正しい開発方針を取れていない
  • ベンダーや協力会社に対して不適切な指示を出してしまう。
  • 技術がわからないので協力会社の成果物を全くレビューできない。
  • 意味のない・効果の薄いドキュメントや報告書を大量に作らせてしまったりしている。

 このアーキテクト不在問題は SoR 型システム開発の改善を考えていく上で極めて根深い問題です。これについては、de:code 2017 DO-08 セッションにて詳細に取り扱っているのでご確認いただければと思うのですが、こうした状況を改善していくための基本的な手立ては以下の 2 つです。

  • 現在のプロマネや業務 SE の使っている、10 年前から進化していない SI 方法論を、近代的なものにリニューアルしていく。
  • 社内に技術がわかるアーキテクトを育てて保有していく。

 これらに関するマイクロソフト エンタープライズサービスの取り組みを私は「開発近代化サービス」と銘打っているのですが、具体的には以下のようなコンサルティングをお客様向けに実施しています。

  • A. 中堅層のプロマネ・業務 SE の再教育プログラム
  • B. アーキテクト育成プログラム

 以下に順に解説します。

[A. 中堅層のプロマネ・業務 SE の再教育プログラム]

 前述したように、モノづくりのやり方が変われば、設計手法やプロマネ手法もそれに合わせた見直しが必要になります。だからこそ、手持ちの仕事で忙殺されている業務 SE やプロマネの方々を、アーキテクトが技術面から下支えし、近代的な SI を実践していくことが必要なのですが、現状を踏まえると、以下の 2 つの問題があります。

  • そもそもアーキテクトが SIer の中にいない(or 不足している)
  • そもそも SoR 型システム開発における、「近代的な SI 手法」が(世の中的に)十分に整備されていない

 これらのうち、後者は非常に大きな問題です。先に述べたように、欧米は内製ベースの SoE 型システム開発を前提として、アジャイルや DevOps といった手法・プラクティス群を開発・整備し続けてきました。しかしその一方で、受発注ベースの SoR 型システム開発に関する「近代的な SI 手法」の改善に関しては手つかずで放置されており、全体としてはほとんど進化していません。私見ですが、おそらくこの問題は日本固有の問題ではなくグローバル共通の課題であり、これに対するソリューションは誰かが先陣を切って新しく作っていかなければならない状況である、と思います。そしてその先陣を切れるのは、SoE/SoR 型システム開発が共存する国である日本をおいて他にないだろう、とも考えています。

 なぜかというと、先に述べたように、SoR 型システム開発手法を進化させていく際には、SoE 型システム開発向けとして開発された各種の DevOps 系プラクティス群が参考になります。右から左に導入することはできませんが、本質論を踏まえて取捨選択しながら取り込んだり融合させていくことは十分にできるわけで(具体的な手法は de:code イベントセッションを参照)、こうした「SoR 型システム開発向けの近代的な SI 手法」を、自社の状況に併せて作り上げていくことが、SIer や IT 子会社の多くに求められている、と考えています。そしてこうした「他国/他社のいいところを参考にして、自分たちのやり方を上手に進化させていく」ことは、もともと日本のお家芸、だったのではないでしょうか?

 幸い、私はマイクロソフトというグローバル企業に所属しており本社の R&D 部門の手法の話やDevOps 系プラクティスについて聞く機会が多いこと、また日本のいわゆるエンプラ系システム開発現場に向けたコンサルティングサービスを 15 年以上手掛けていること、そしてこの領域をなんとかしたいという思いがあり、日本独自のソリューションとして開発近代化サービスを開発し、展開しています。具体的には、主に現場経験 10 年程度のプロマネ・業務 SE を対象として、V 字型モデル開発の基本を、現在の開発技術やトレンドに沿った実践的手法と共に学び直す、という再教育プログラムを開発・デリバリしています(実は de:code の昨年・今年の私のセッションは、この再教育プログラムからごく一部を切り出したものです)。

image

 内容としては、開発プロセス、チーム編成、外部設計、内部設計、ソフトウェアテストなどについて、日本の現状や欧米でのベストプラクティス、また日本の様々な現場に見られる課題の解決のヒントなどをひたすら紹介していく、というものになっています。実装方法を解説するものではないので、コードはほとんど出てこず、それ故に特にマイクロソフト技術に縛られる内容にもなっていません。実際の開発現場において「なぜそれをしなければならないのか」というところに踏み込むことで、プロマネ・業務 SE として外してはならない要点、SI において『幹』となる考え方やタスクをきちんと理解・習得していただくことを目的としています。

 また、こちらは単発トレーニングとして実施することも多いのですが、より踏み込んだお客様では、この内容を元に実際のお客様プロジェクトにおける設計・テストドキュメントの改善レビューを行い、より一層深い理解・改善をしていただいていることもあります。

[B. アーキテクト育成プログラム]

 もう 1 つは、そもそもの問題の根幹であるアーキテクト不足を解消するために、社内アーキテクトを育成していくプログラムです。アーキテクト育成のための設計・実装技術のスキルトランスファーといった昔ながらのサービスも手掛けていますが、アーキテクトとしての重要スキルの一つであるコンサルテーションスキルについても OJT での育成サービスを行っています。

 もう少し具体的に書くと、SIer や IT 子会社におけるアーキテクトは非常に貴重な人材であるため、共通部門(CoE, Center of Excellence)に集約され、下図のような様々なミッションを担っていることがよくあります。こうした人材はアーキテクトとしての活動を通して、究極的には現場向けの社内コンサルタントになっていくことが望ましいのですが、一朝一夕にコンサルタントとしての立ち振る舞いができるようになるわけでもありません。そこで弊社コンサルタントが『お手本』となって、ガイドラインの作成支援や実際の現場プロジェクト支援などに参画し、メンバーを育てていくといったこともしています。

image


■ ② 先進的ビジネスのための SoE 型開発への取り組み ~PoC ラボによるアイディアの早期具現化~

 さてここまで述べてきたように、SoR 型システム開発の改善は、従来のやり方の延長線で考えていくことが可能です。しかし SoE 型システム開発への取り組みに関しては、全く異なる発想とアプローチが必要になります。これについて、ビジネス的な背景も含めて少し掘り下げます。

 ご存じのように、昨今の IT(デジタル)は人々の生活(アナログ)をあらゆる面で支え、よりよいものにしています。このような変化は「デジタル変革」(デジタルトランスフォーメーション)と呼ばれていますが、このデジタル変革は極めて急激に進むという特徴を持っています。具体的に言うと、ひと昔前であれば Facebook や Google、そして最近であれば Uber や Airbnb などといった、いわゆるスタートアップと呼ばれる企業は、恐るべき速度でサービスを拡大してきました。そして既存企業がその速度についていけず、あっという間に既存ビジネスが破壊されるということが現実のものとなっています。

 この既存ビジネスの破壊において重要なポイントの一つは、そこで使われている技術は必ずしも新しいものではない、という点です。よく語られることですが、例えば 2007 年に発売された iPhone は、当時の既存技術を組み合わせて作られており、iPhone のために何か全く新しい技術が発明されたというわけではありません。この例からわかるように、現代は、すでに多数の「発明」(AI や VR などの革新的技術)が存在しており、それが「ユースケース」(使い方・ニーズ)の発見を待っている、そしてそれを最初に見つけてビジネス化できた人がイノベーションを起こす、という形になっているわけです。

image

 こうした「ニーズ」の発見には Try & Error による試行錯誤が欠かせませんが、昨今はクラウドの登場、デバイスのコモディティ化などによって IT 技術の利用コストが大幅に低減しており、挑戦のためのハードルが昔に比べて圧倒的に下がっています。その結果、数多のスタートアップ企業がリスクをどんどん取って、イノベーションへの挑戦(簡単に言えば「一発当てる」ための挑戦)を繰り返している、という状況が生まれています。米国におけるスタートアップブームの背景にはこうした IT 環境の変化があるわけです(このあたりは馬田さんの良著「逆説のスタートアップ思考」にて詳細に解説されていますので、興味がある方はご一読いただくとよいと思います)。

 上記のような状況を元に考えると、先進的テクノロジを使った新しいビジネスの取り組みに必要なポイントは以下の 3 つであり、マイクロソフト エンタープライズサービスではこれらに対応するサービスをそれぞれ展開しています。

image

 以下に順に解説します。

[A. 最新テクノロジの理解(インベンションの理解)]

 革新的なサービスを考える際、シーズ(種)となる最新技術を知ることは極めて重要です。特に事業部門のお客様に近ければ近いほど、ビジネスに深い造詣がある一方、最新技術を知らないことが多くなるため、こうした最前線の方々の業務知識と、最新技術とを突き合わせてみることがより重要になってきます。

 とはいえ数多の技術をすべて把握することは非現実的でもあります。こうしたことから、弊社では定着期に入りつつある技術を効率的に把握するための最新技術セミナーを用意しています。このセミナーによって、ビジネスアイディアとマッチングできる最新技術を探っていくわけです。

image

[B. ビジネスニーズの深掘り(ユースケースの模索)]

 2000 年以降に成人になった若者の方々をミレニアル世代と呼びますが、消費世代に差し掛かってきたミレニアル世代の人々の考え方は、旧世代の人たちとは大きく異なることが知られています。よく言われることとして、デジタルネイティブである、所有欲がない(シェア・利用)、出世よりワークライフバランス、自分が納得したものを消費する、ソーシャルで緩く繋がる、といったことが挙げられますが、こうした価値観の変化にうまく追随できていない既存企業が増えつつあります。

 実はこの問題はミレニアル世代に限った話ではなく、リーマンショック以降の生活様式の変化や人々の価値観の変化といったものに追随できないことで、新しい企業にお客様を奪われていくことがしばしば発生します。その大きな理由の一つとして、「従来型のビジネスを熟知していること」、すなわち過去の成功体験が発想・思考の妨げになることが挙げられます。こうした課題を解決していくためには、なによりお客様を深く理解すること、そしてそこから隠れたビジネスニーズを深掘り・発見していくことが必要になりますが、そのための手法として最近着目されているのが、デザイン思考(Design Thinking)と呼ばれる手法です。

 本論から逸れるため、ここではデザイン思考の詳細に立ち入ることは避けますが、もともとデザイン思考は優秀なデザイナさんによる物事の「思考方法」「考え方」を真似るところからスタートしており、下図に示すような発展の経緯を辿ってビジネスの世界へと入ってきています。このことからもわかるように、デザイン思考そのものは必ずしも IT に限定されるものではないのですが、近年では、こうした「優秀なデザイナさんによる物事の思考方法」を、各業界に特化した形で進化・発展させ、より使いやすい手法(プロセス)としたものが各社から提唱・提供されているようになってきました。

image

 マイクロソフトでもこうした流れを受け、デジタルエンビジョニングワークショップ(DEWS)という手法を開発し、サービスを提供しています。これはお客様への深い洞察を元に、新たなイノベーションを IT 技術との組み合わせにより生み出していくビジネスデザイン手法を体系化したもので、デジタルトランスフォーメーションへの足掛かりとして、現在マイクロソフト エンタープライズサービスよりサービスを提供しています。

image

[C. プロトタイプによる PoC 検証(Try & Error による模索)]

 さて、最新技術とユーザニーズの掛け合わせから生まれたアイディアは、PoC (Proof of Concept、プロトタイピングによる概念検証)により素早く具現化・検証していくことが必要です。前述したように、今日では IT 実現コストの低減により「まずは試してみる」ことが昔に比べて圧倒的に容易化しています。アイディアを素早く形にして試しいみることで、お客様からの F/B を早期に得ることができ、より優れたアイディアやビジネスに昇華させていくことができるわけです。

 こうしたアイディアの素早い具現化のためには、以下の 2 つが必要不可欠です。

  • 社内 PoC ラボセンターの設置
    アイディアの具現化は素早く社内で行うことが必要であり、いちいち請負型でベンダーに発注することは非現実的です。このため、PoC アプリ開発チームは社内に持つ必要があります。(社内アーキテクトは SoR 型システム開発におけるアーキテクトロールだけでなく、こうした取り組みでも活躍してもらう(=アーキテクトの人たちが先端技術を使って直接プロトタイプを実装していってもらう)ことになります。)
  • 高速開発基盤の整備
    Azure などのクラウドをフル活用した開発基盤を整備し、PoC アプリ開発チームが作成したプロトタイプをすぐさま配置してテストできる環境を整えておく必要があります。

 なおこれらの取り組みは、SoE 型システム開発における PoC 検証はもとより、従来の SoR 型システム開発においても有効性が高いです。なぜなら PoC によるプロトタイピングで十分に UI などの要件を煮詰めたうえでベンダーに発注することにより、見積もりブレや手戻りなどのプロジェクトリスクを極小化することができるためです。

image

 弊社マイクロソフト エンタープライズサービスでは、上に示したような取り組みをしやすくするため、以下のようなサービスを展開しています。

  • PoC ラボセンターの提供
    弊社コンサルタントチームが(場合によってはお客様先に常駐して)実際に PoC を回し、高速にお客様アイディアを具現化していく。
  • Azure PaaS 開発基盤の整備
    Azure を利用する場合のリファレンスアーキテクチャガイドの整備などを実施する。

■ マネジメント層に求められる役割

 さて、ここまでマイクロソフト エンタープライズサービスが手掛けてきているエンプラ系 SIer 向けの取り組みをご紹介してきましたが、こうした取り組みを実際に進める際には、マネジメント層にも重要な役割を担っていただく必要が出てきます。それは、SoR 型システム開発と SoE 型システム開発とを分離し、そこに適切な経営レベルの判断を入れる、という点なのですが、このポイントは非常に重要なため、最後に解説して締めくくっていきたいと思います。

 前述した SoR 型システム開発への取り組みは漸進的アプローチ、すなわち従来の取り組みを改善していくアプローチである、ということができます。一方で、SoE 型システム開発への取り組みは革新的アプローチであり、リスクを取って進めていくべき性格が強いものです。このため、適用対象となるプロジェクトも違えば、個々の社員の向き不向きも異なってきます。もちろんノウハウを交換しあうことは重要ですが、全社一律のルールで管理しようとすると、様々なところに無理が生じます。故に、会社の中で、SoR 型システム開発への取り組みと、SoE 型システム開発への取り組みとをきちんと分けてそれぞれ考えることがまず重要になるわけです。

 また、特に SoE 型システム開発でイノベーションへ取り組む場合には、マネジメント層の『投資判断』が必要になることもしばしば発生します。イノベーションに必要なユースケースの発見には、相当な Try & Error が求められるのですが、実際のスタートアップ企業の打率はかなり低く(例えば 100 社に投資しても 1 社成功するかどうか、しかしその成功する 1 社は 1 万倍のリターンがある、といった具合)、ハイリスク・ハイリターンの博打的な側面があります。こうしたところに企業としての合理的な判断(例えば ROI や短期的なリターンなど)を求めると、イノベーションへの取り組みは必然的に失敗します(これをイノベーションのジレンマと呼びます)。

 こうした問題から最近着目されているのが「バーベル戦略」という考え方です。これは大半のリソースは従来通り安全牌にかけておき、一定のリソースをハイリスク・ハイリターンに賭けてイノベーションを目指す、というものです。先に述べたように、スタートアップ的な領域への投資では一発の大当たりが他のすべての失敗を帳消しにするものの、実際には「全部失敗することもありうる」わけで、余剰体力が残っているうちに、余剰体力で新しいビジネスの芽を見つけるために頑張るわけです。

image

 マネジメントは何かと現場に ROI を求める傾向がありますが、ROI が算出できるのであれば、マネジメントの判断など不要です原理的に白黒が付けられない投資(リソース投入など)に対して、自らの裁量、そして KKD (勘と経験と度胸)で判断をすることこそがマネジメントの仕事である、私はそう考えています。(← KKD はそういうところで使うべきものだと私は思います)

 本 blog で解説したような取り組みでは、必ず投資モードで行う作業が発生すると思います……が、そうした取り組みをいつ、どの程度、どのような形でやるのか。それを判断しサポートするのはマネジメント層の役割であり、経営判断がどうしても必要な領域になります。マネジメント層の方々には、ぜひ現場メンバーの積極的な姿勢や取り組みをサポートし、会社やチームの活性化につなげていただければと思います。


■ まとめ

 本 blog では、私が所属するエンタープライズサービス統括本部のコンサルティングの取り組みのご紹介を通して、『変わらない開発現場』に苦しむ IT 子会社さんや SIer さんの改善の取り組みのヒントになる情報を執筆してみました。要点をまとめると、以下の通りとなります。

  • システム開発の改善を考える場合には、SoE 型システム開発と SoR 型システム開発とに分けて考える。
    • SoE 型システム = 「コンシューマ向け、フロントエンド、オープン、B2C」といった特性を持つシステム、コンシューマ向け Web サイトやゲームアプリなど
    • SoR 型システム = 「エンタープライズ系、バックエンド、基幹系、B2B/B2E」といった特性を持つシステム、金融系勘定システムなど
    • これら 2 つは開発特性が全く異なるため、分けて考える必要がある
  • 欧米の手法を右から左に導入するのではなく、システム開発特性を意識した上で導入する。
    • アジャイルや DevOps などの手法は、SoE 型システム開発の文化の中で進化・発展してきたもの。
    • 日本国内には SoE 型システム開発と SoR 型システム開発の両方が共存している。このため、SoR 型システム開発へ欧米の手法を導入する際には、各種プラクティスを取捨選択しながら段階的に導入し、現場改善につなげていく必要がある。
  • SoR 型システム開発の改善は、以下の 2 軸で考えていくとよい。
    • 現在のプロマネや業務 SE の使っている、10 年前から変わらない SI 方法論を、近代的な SI 方法論にリニューアルしていく。
    • 社内に技術がわかるアーキテクトを育てて保有していく。
  • SoE 型システム開発の改善は、以下の 3 軸で考えていくとよい。
    • 最新テクノロジの理解(インベンションの理解)
    • ビジネスニーズの深掘り(ユースケースの模索)
    • プロトタイプによる PoC 検証(Try & Error による模索)

 システム開発現場の悩みは、SIer ごと、開発現場ごとにそれぞれ異なると思います。本エントリの解説を参考にしていただいて、現場改善のための具体的な戦略立案、そして実行へとつなげていただければ幸いです。

『変わらない開発現場』シリーズ 情報ポインタ一覧

$
0
0

昨日ですが、ようやく de:code 2017 で実施したセッション ppt, ビデオが公開されました。先日アップした blog エントリと併せて、昨年の de:code 2016 から始まった『変わらない開発現場』シリーズは一通りこれでやり尽くした形になりますが、ここで改めて、関連する情報ポインタ一覧を整理しておきたいと思います。

現場の方が初めて見る場合には ① de:code 2016 CLT-016 から、マネジメントや上司の方にオススメする場合には ② de:code 2017 DO-08 あるいは ③ からまずご覧いただき、興味に応じて他の情報を見ていただくとよいと思います。社内勉強会などにぜひご活用ください。これらの情報が、皆様の開発現場の改善に少しでもご活用いただけることを願っております。

※ ご意見・ご感想・F/B・拡散など大歓迎です。Twitter @nakama00、はてブ、あるいはこの blog などでぜひお気軽にコメントなどをお寄せください。


① de:code 2016 CLT-016
拝啓『変わらない開発現場』を嘆く皆様へ ~エンプラ系 SI 開発現場の「今」を変えていくために~
[ビデオ]
https://channel9.msdn.com/Events/de-code/2016/CLT-016
[ppt] https://docs.com/decode2016/9106
[はてブ] http://b.hatena.ne.jp/entry/s/channel9.msdn.com/Events/de-code/2016/CLT-016
[主な対象者] SIer 現場担当者、SIer マネジメント層
[内容] 設計・テストに関する実践的な改善方法

このエントリーをはてなブックマークに追加


② de:code 2017 DO-08
『変わらない開発現場』を変えていくために ~エンプラ系レガシー SIer のための DevOps 再入門~
[ビデオ]
https://channel9.msdn.com/Events/de-code/2017/DO08
[ppt] https://www.slideshare.net/decode2017/do08-sier-devops
[はてブ]http://b.hatena.ne.jp/entry/s/channel9.msdn.com/Events/de-code/2017/DO08
[主な対象者] SIer マネジメント層、SIer 現場担当者
[内容] SIer の技術力空洞化問題と近代的プロマネ技術

このエントリーをはてなブックマークに追加


③ 拝啓『変わらない開発現場』を嘆く皆様へ
~変わっていくエンタープライズ系業務システム開発とマイクロソフトエンタープライズサービスの取り組み~
[blog]
https://blogs.msdn.microsoft.com/nakama/2017/05/25/devmodernization/
[はてブ] http://b.hatena.ne.jp/entry/s/blogs.msdn.microsoft.com/nakama/2017/05/25/devmodernization/
[主な対象者] SIer マネジメント層
[内容] SIer のマネジメント層が考えるべき今後に向けた取り組み方
image
このエントリーをはてなブックマークに追加


④ 続・拝啓『変わらない開発現場』を嘆く皆様へ ~ ウォータフォール & アジャイル編~
[blog]
https://blogs.msdn.microsoft.com/nakama/2016/06/24/waterfall/
[はてブ] http://b.hatena.ne.jp/entry/s/blogs.msdn.microsoft.com/nakama/2016/06/24/waterfall/
[対象者] SIer マネジメント層、SIer 現場担当者
[内容] ウォータフォールとアジャイルの特性の違い、 受発注型 SI ビジネスの中での現実的な Agile・Scrum の活用技術の提言


このエントリーをはてなブックマークに追加


⑤ 『変わらない開発現場』を変えていくために。
[blog]https://blogs.msdn.microsoft.com/nakama/2016/07/03/nextstep/
[はてブ] http://b.hatena.ne.jp/entry/s/blogs.msdn.microsoft.com/nakama/2016/07/03/nextstep/
[対象者] SIer 現場担当者
[内容] 上司の説得方法、現場エンジニアとマネジメントの思考の違い
このエントリーをはてなブックマークに追加


⑥ TechSummit 2016 DEP-010
進化し続ける OS インフラへの挑戦 ~WaaS 時代のアプリ互換の考え方~
[ビデオ] https://www.youtube.com/watch?v=8GAJTT_BJQk
[ppt] https://docs.com/mstechsummit16/2357
[内容] 開発近代化の必要性、 CI/CD、カナリアサーバなどの DevOps プラクティスの実践的デモ


⑦ 開発系エンジニアのスキルロードマップ
[blog]
https://blogs.msdn.microsoft.com/nakama/2011/11/24/part-1/
[はてブ] http://b.hatena.ne.jp/entry/blogs.msdn.com/b/nakama/archive/2011/11/25/skill-roadmap-part-1.aspx
[対象者] SIer 現場担当者、SIer マネジメント層
[内容] SIer で求められる主なスキルセットと学習ロードマップ
image
このエントリーをはてなブックマークに追加

業務システム開発 モダナイゼーションガイド

$
0
0

あいかわらず稀にしか更新しない blog で大変恐縮なのですが、自身 10 冊目(!)となる書籍が発刊されました!(前回の Azure 本から実に 7 年ぶりという遅筆ぶりなのですが;。)

image

正式タイトルは「業務システム開発 モダナイゼーションガイド~非効率な日本のSIを変革する実践的ベストプラクティス~」。今回の書籍のテーマは、日本の SIer の旧態依然とした Excel まみれの業務システム開発の手法を近代化(モダナイゼーション)して生産性を高めようというもので、以前、弊社のイベント de:code で大変反響のあった「拝啓『変わらない開発現場』を嘆く皆様へ」のフルセット版のようなものになります。タイトルだけでは書籍の内容がさっぱりわからん! という方が多いのではないかということもあり、こちらに本書のまえがき全文を掲載したいと思います。ご興味のある方は、ぜひ本屋でお手に取っていただければ嬉しいです。(日経 BP さんのサイトでの紹介はこちらです。社内エンジニアの育成テキストなどとしてもご活用いただけると思いますので、もしバルク購入したいというご要望がありましたら、こちらのお問い合わせフォームから日経 BP さんにご連絡ください。)

# ちなみにどうでもいいつぶやきですが、個人での印税受け取りは放棄しているため、「儲けた印税で飲みに連れていってください!」的なリクエストはなしで & ワリカンでお願いします;。

本書の執筆にあたっては、日経 BP さんをはじめ、社内外のたくさんの皆様からご協力をいただきました。改めてこの場を借りてお礼申し上げます。ありがとうございましたm(_ _)m。また、イイネ! ダメダネ! とか、この本いいよ! ここ間違ってるよ! ここはもっといいやり方あるよ! とかありましたら、Facebook/Twitter/この記事のコメント/はてブなどで反応いただけるとめちゃめちゃ嬉しい & 励みになります。

私の SI 業界での 20 年間の経験の集大成として執筆しました。情報密度がかなり高い本になっていますので、社内勉強会でみんなで検討しながら読むなど、ぜひ周りの方とも情報共有しながら現場でご活用いただければと思います。この書籍が、皆さまの開発現場を少しでも改善するきっかけになること、そしてこの本を踏み台にして、よりよいシステム開発方法論が建設的に議論されていくことを心から願っています。では、以下、まえがきです。


業務システム開発 モダナイゼーションガイド~非効率な日本のSIを変革する実践的ベストプラクティス~ まえがきより


■ はじめに ~『変わらない開発現場』を嘆く皆様へ~

 「日本の SI ビジネスは崩壊前夜」
 「日本の SI はガラパゴス」
 「日本の SIer の余命はあと5年」
 「ウォータフォールには何のメリットもない」
 「日本のエンジニアはイノベーティブじゃない」

 ネットや雑誌で毎日のように揶揄される日本のエンプラ系 SI(システムインテグレーション)業界。古くは「現代の 3K(キツい・帰れない・給料が安い)」「デジタル土方」、そして最近では「SI オワコン」「SI 終末論」など、次々と生み出される悲惨なキーワード。これらを見るたびに、心を打ち砕かれている現場エンジニアはかなり多いのではないだろうか。

 実際、日本の SI 開発現場は確かにひどい。下図は、数年前にある情報システム子会社の幹部に対して、その会社の実際の現場で発生している問題点を整理・説明するために筆者が作ったものである。ところがこのイラストを他のお客様(SIer や情シス子会社)や弊社内の同僚のコンサルに見せてみると、自社あるいは自分が担当するお客様でも似たような、あるいは全く同様の問題が起こっているという。

image

 筆者が SI 業界に身を置くようになって約 20 年になるが、実際、コンサルとして日本の様々な開発現場に行ってみると、判を押したかのように似たような問題にぶち当たる。一定の年次になったというだけで十分な知識もないのに「上級 SE」にさせられたプロパー(正社員)が、コストと時間に追われて場当たり的な作業指示を出し、協力会社だけでなく、指示を出した自分自身も作業効率の悪さに苦しむ。そして前述したような心無い罵倒に打ちひしがれながらも、なまじ当たっている面があるだけに言い返せずますます悶々とする。こんなやり方を続けていたら、確かに日本の SI が崩壊するのも時間の問題である。

 無論、SIer や情シス子会社のプロパーや日本の SI に関わるエンジニアとて、明らかに沈むとわかっている沈没船に身を委ねたいわけではない。少しでも現場を改善したい、少しでもプロジェクトがうまくいくようにしたいと、日々もがき苦しみながら努力を続けている人たちの方が圧倒的多数だろう。だがしかし、実際には日々の業務をなんとかこなすのが精いっぱいで、抜本的な業務改善までには手が回らない。ニアショアやオフショアを使うだけではもはやインパクトのあるコストメリットが出せなくなりつつある上に、際限のないコストカットの波により現場の労働力が枯渇していく。そんな過酷な現実の中で、どうやって高い生産性と俊敏性を実現させていくかについて悩んでいる現場エンジニアやマネジメントは非常に多いはずである。

 こうした SIer や情シス子会社の日々の悩みに対して、果たしてアジャイルや DevOps といった、欧米流のソリューション(プラクティス)が「そのまま」解決策になるのだろうか? 筆者は、この答えは明らかにノーであると考える。少数精鋭で新しいビジネスを模索し続ける、内製前提の SoE 型システム開発の現場から生まれたソリューション(プラクティス)は、システム化対象となる業務量・業務数の多さから少数精鋭での開発が困難となる、請負前提の SoR 型システム開発の現場に必ずしもそのまま適用できるものではない。

 そもそも多くの SIer や情シス子会社が悩んでいるのは、様々な事情や制約によって高スキルのエンジニアだけを揃えられるわけではないという状況下において、いかに互いのスキル(専門性)を補完し、チーム全体・プロジェクト全体のアウトプットを最大化していくかという命題である。契約形態(内製型と請負型)、開発規模、揃えられる人員とスキルセットといった、数々の前提条件が異なる欧米型ソリューションをそのまま持ってきて、SIer や情シス子会社の悩めるプロパーたちに対してマウントポジションで殴り掛かるのは筋違いというものであろう。だが、まるで欧米礼賛とも言えるこうした言説は未だ後を絶たず、特にネット上では噛み合わない議論から、不毛なけなし合いへと発展・炎上する例がしばしば見受けられる。こんな状況がいつまで続けばよいのだろうか。

 

■ SI 技術(システムインテグレーション技術)の重要性

 本来、技術進化の恩恵は、IT 技術を駆使するシステム業界に属するエンジニアが最も享受できるはずである。にもかかわらず、その恩恵をほとんど受けられていないのは何故か。それは、日本のエンプラ系開発現場では、なんとなく非効率的と感じながらも、数十年前と本質的には大きく変わらないやり方を繰り返し続けているからではないだろうか。

image

 そしてこの状況を生み出している最大の原因は、日本のエンプラ系 SI の商慣習(多重請負構造)における『上流』の仕事のやり方が、ほとんど進化していないことにあると筆者は考えている。

image

 Visual Studio や Azure といった開発技術(実装技術、モノ作りの技術)を最終的に触るのは、末端にいる協力会社のエンジニアである。しかし、多重請負構造におけるエンドユーザー企業や IT 部門、SI 関連会社、SIer がこうした開発技術の活用を『妨げる』ようなことをしていては、いつまでたっても開発技術の進化の恩恵を受けることはできない

 当たり前のことだが、モノ作りのやり方が変われば、設計手法やプロマネ手法(いわゆる『SI 技術(システムインテグレーション技術)』)もそれに合わせた見直しが必要になる。近代的な開発技術の進化を理解し、SI技術を進化させ(=近代化し)、エンドユーザー企業に対してそのメリットを提案していくのは、IT 部門や SI 関連会社、SIer の責務である。IT 部門や SIer が技術のことを知り、正しくプロジェクトを立て付けなければ、いつまでたっても現場は非効率的な Excel 設計書と Excel テストから脱却できないのである。

 しかし考えてみると、.NET や Javaなど製品技術を解説する本や、プロマネを対象とした専門書は多数あるが、「SI (システムインテグレーション)」に関する技術的なセオリー全般をバランスよく扱っている書籍が意外に少ない。これには以下のような理由が考えられる。

 ① 日本の商慣習を前提としたシステム開発手法が生み出されていない
 ② そうしたノウハウは(あったとしても)SIer の門外不出となっており情報が流通していない
 ③ ベンダーから提供される情報は実装技術に特化しすぎている
 ④ プロマネ向けに書かれた書籍は設計・実装・テスト技術に触れていないものが多い

 実際、筆者が実際の開発現場に行くと、「なぜこんな基本中の基本が見落とされているのか?」と思うようなことが少なくないが、逆に「そうしたことを短時間で学べる書籍はないか?」と問われると、なかなかおすすめできる書籍がない。これではいつまでたっても日本のシステム開発の現場はなかなか変わらないだろう。こうした現状を打破していくため、筆者が 20 年間かけて、コンサルティングの現場で培ってきたノウハウとベストプラクティスを、可能な限りお伝えしたいと考えて本書を執筆することにした。

 

■ 本書の内容とゴールについて

 本書は、日本の SI 業界における多重請負構造を前提条件とした上で、SI(システムインテグレーション)に必要となる基礎知識やノウハウ(SI 技術)を幅広く提供する。これにより、多重請負構造の上流の立場にある IT 部門や SIer のプロパーが、システム開発を正しい方向に導き、開発現場を近代化できるようになることを目指すものである。

 

■ 本書の構成について

 本書では、以下の 4 つの章を通して、SI 技術に関するノウハウを幅広く提供する。全て通して読んでいただくことが望ましいが、悩んでいるところや興味があるところだけをかいつまんで読んでいただいても構わない。

第 1 章 開発プロセスと V 字型モデル

 ネットの世界では、「イケてない開発 vs イケてる開発」という文脈で、しばしば「ウォータフォール vs アジャイル」の対立構図が取り上げられる。しかしこうした近視眼的な見方では、システム開発の本質を見失う。そこで第1章では、ウォータフォールとアジャイルの特性の違いを解説すると共に、改めて、システム開発の基本とも言える V 字モデルの重要性について振り返る。そして V 字型モデルの段階的詳細化の最大の問題・課題とも言える「後工程の予測・推定の必要性」について述べ、手戻りを軽減・防止するための基本的な対策方針を説明する。

 また、開発プロセスのテーラリングにおいては、SoR 型システム開発と SoE 型システム開発とを正しく分けて考えることが最初の起点となる。これらのシステム開発の特性の違いを解説すると共に、日本の SI がガラパゴスなのではなく、むしろ欧米の SI こそがガラパゴスであることを述べる。これを通して、現在の日本のエンプラ系 SI 業界が行うべきことが、「SoE 型システム開発で生まれた様々なベストプラクティスのエッセンスを、SoR 型システム開発へ還元すること」であることを説明する。

第 2 章 フェーズとロールモデル

 プロジェクトの成否は、最初の建付け(チーム編成とタスク検討)により大きく左右されるが、実際の開発現場を見ると、チーム編成にしてもタスク検討にしても、基本的なセオリーが守られていないことが少なからずある。よくある問題としては、「専門性(ロール)」を意識しないアサインや、非機能要件に基づく設計プロセスである「方式設計」の漏れ、そして技術力の軽視から来るアーキテクトの不在問題などがある。

 そこで第 2 章では、業務システム開発の骨格となる、2つのタスクの流れ(機能要件に基づく流れと非機能要件に基づく流れ)と、5 つのロール(業務 SE、アーキテクト、デベロッパー、テスター、プロマネ)について解説する。そして、チーム編成で特によく見られるいくつかの問題を取り上げ、正しくチーム編成とタスクを建て付けるための基本を説明する。

第 3 章 各タスクの実施の要点

 開発生産性の改善には、(当たり前だが)作業の手戻り防止と、ムダな作業の排除が欠かせない。にもかかわらず実際の現場では、Excel での画面設計書をひたすら手でこねくり回して時間を無駄にしたり、実施証跡を取るためにテスト結果のスクリーンショットを Excel にひたすら貼り付けたり、保守されることもなく腐っていく内部設計書を粗製乱造しまくるといったデタラメが横行しているのが実態である。こうしたムリ・ムダ・ムラが改善されない大きな理由のひとつは、「そのタスクがなぜ必要なのか(Why)」「そのタスクでは少なくとも何を検討して何を作る必要があるのか(What)」が十分に議論・理解されていないことである。手戻り防止とムダな作業の排除には、チーム内での根拠のある議論が必要になるが、そのための基礎知識が足りていない場合が非常に多いと考えられる。

 そこで第 3 章では、要件定義、外部設計、方式設計、内部設計、実装・単体機能テスト、結合機能テスト、システムテストのそれぞれについて、各タスクの実施の要点を解説する。これにより、「根拠のある作業」「根拠のある SI」を実現できるようにする。この章では、SoE 型開発現場で生まれたベストプラクティスを、SoR 型開発現場に取り込むための多数のTipsを織り込んでいる。この章に書かれた内容をきちんと実践するだけでも、QCD(品質・コスト・期間)はかなり改善できるはずである。

 また第 3 章の最後では、近代的なインフラや運用管理の基礎概念についても解説する。優れたシステム開発のための基礎となる「運用環境第一主義(Production First Mindset)」の考え方について触れ、その実現に必要となる近代的なインフラや運用管理の要点を解説する。これらが正しく理解できると、クラウドや DevOps の必要性も、必然的に理解できるはずである。

第 4 章 システム開発現場の近代化に向けて

 実際の現場では、「今のやり方を変える必要などない」という、論理的とは言えない部課長の鶴の一声で、現場改善の芽はもちろんのこと、若手社員のやる気まで潰してしまうケースも少なくない。しかし、現場を変えていけるのは現場のエンジニアでしかなく、また彼らを勇気づけ、ルールや仕組みを変えていけるのはマネジメントでしかない。本来、両者は二人三脚が必要であり、そのためには根拠のある議論に基づいた歩み寄りが必要となる。

 そこで本書の締めくくりとなる第 4 章では、会社や組織、そして個人が何を意識し、どこを目指していけばよいのかの指針について、筆者の見解を述べる。具体的には、SoR 型システム開発では中堅層のプロマネや業務 SE の再教育、社内アーキテクトの育成、初級エンジニアの設計・実装スキルの底上げ、成果物の品質確保プロセスの作り込みなどが必要であること、また SoE 型システム開発では最新テクノロジの要点の理解、デザイン思考の利用などによるビジネスニーズの深掘り、プロトタイピングによる概念実証を行うための社内 PoC チームなどが必要であることを解説していく。そしてエンジニアやマネジメント個々人がどのような考え方を持って何に取り組むべきかを説明する。

 第 4 章は第 1 章から第 3 章を踏まえて読んだ方がより納得感があると思われるが、忙しいマネジメントや上席の方は、この章だけをピックアップして読んでいただいてもよいだろう。

 

■ 本書の解説について

 筆者はここまで .NET 関連の書籍を 9 冊執筆してきているが、10 冊目となる本書では、.NET や Azure といった実装技術そのものに関する説明はほとんど行わない。“How” ではなく “Why”、すなわちなぜその作業をしなければならないのか? にフォーカスした解説を行う。理由は以下の通りである。

 ・ 具体的なやり方は、その気になって調べればいくらでも情報が見つかる。
 ・ なぜそれをする必要があるのか? を理解すると、自信を持ってシステム開発に取り組める。

 SI(システム開発)のそれぞれの作業(タスク)には、意味と根拠が必ずある。一つ一つの作業の「理由」「意味」を正しく理解して、作業のムリ・ムダ・ムラを排除すること、そして「あるべき論」を正しく理解した上で、現実の「制約条件」と組み合わせ、落としどころを探ることで、『根拠のある作業』『根拠のある SI』を実現することができる。

 

■ 本書で想定している読者と前提知識について

 本書は主に IT 部門や SIer のプロパー(業務 SE、プロマネ、アーキテクトなど)をターゲットとして執筆している。前述したように、発注元になる IT 部門や SIer のプロパーが正しいアプローチを知らないと、システム開発の現場を適切な方向に導くことができず、新しい開発技術の恩恵を受けることができなくなるためである。

 また、現場経験年数としては5年以上の方を想定して執筆している。ある程度の現場経験があると、肌感覚として理解できる部分、納得できる部分がたくさん出てくるのではないかと思う。完全な新卒エンジニアでも読めないことはないと思われるが、その場合には、数年後にまた本書を読み返してみていただけると、違った発見があるかもしれない。

 逆に、業務 SE、アーキテクト、プロマネなどそれぞれの道の「プロ」からすると、自分の専門領域に関する情報としては物足りない部分があるはずである。本書は特定のロールの「プロ」向けに書いた書籍(例えばプロマネのプロ向けに書いた書籍)ではなく、どのようなロールの人であっても必ず知っておいていただきたい「SI 技術の基礎」をまとめた書籍である。現場経験年数が長い方は、ご自身の専門ロール以外の部分を読んでいただけると幸いである。

 

■ 本書の免責事項について

 日本人は書籍に対してバイブル的な存在を求める傾向があると感じているが、残念ながらそうした情報は全世界どこを見回してみてもなかなか存在しないのが実情である。そうした中で、筆者の独自研究と現場経験に基づいて、筆者自身が「こうした情報があればよいのに」という希望を形にしたのが本書(およびこの書籍を書くための元ネタとなっている、マイクロソフトのコンサルティングサービスが提供する Visual Studio Workshop)である。

 このため、本書には筆者個人の考えや解釈などが多く含まれているが、これはあくまで筆者の個人的見解であり、必ずしもマイクロソフトおよびコンサルティングサービスとしての公式見解というわけではない。この点に関しては、ご容赦いただければと思う。

 なお、書籍をいきなり読むのが少しつらい、という方には、以下のサイトにまとめてある各種イベントビデオもご活用いただければと思う。扱っているトピックは限定的だが、本書でどのような話を取り扱っているのかの一端を短時間でご確認いただくことができると思う。

 ・ 『変わらない開発現場』シリーズ 情報ポインタ一覧
 https://blogs.msdn.microsoft.com/nakama/2017/06/17/sier-modernization/

 

■ 本書で利用しているツールや画面スナップショットについて

 先に、.NET や Azure といった実装技術そのものに関する説明はほとんど行わないと書いたが、ある程度具体的なツールや手法を示した方が理解しやすいものについては、マイクロソフトの製品やサービスを例にとって説明を行っている。筆者は基本的にマイクロソフト以外の技術に明るくないため、解説はマイクロソフト製品をベースに行っているが、同様のことは(おそらく手間は増えるだろうが)OSS 系ツールや他社製テクノロジであっても実現できるだろう。

 また広範な領域の情報を限られた時間で執筆している関係で、すでに紹介しているツールや画面スナップショットが古くなっているものもある。また、Azure や Visual Studio、Visual Studio Team Services(VSTS)はどんどん進化を続けるため、読者の皆様が本書を読むころにはすでに古くなってしまっている可能性が高い。しかし、本書で解説している「SIの基本」はさほど変わらないはずである。実際に現場で「SI の基本」を実践する際には、その時点での最新のツールを調査した上で利用していただきたい。

 

■ 本書に関するご意見・ご感想などについて

 限られた時間での執筆であることに加え、筆者の経験不足・知識不足により、書き足らずのところや、さらに良い手法が存在するところもあると思う。こうした部分については、ぜひ本書を素材または踏み台として、ネット上や社内勉強会、各種コミュニティなどでオープンに議論していただければと考えている。

 本書に関するご意見やご感想などは、以下のメールアドレス宛にお寄せ頂ければ幸いである。今後の執筆内容などに可能な限り反映させ、より分かりやすい、より良いコンテンツを提供していきたいと考えている。

 ・ 日経BP社
 nsp@nikkeibp.co.jp

 

■ 最後に

 特に、開発方法論に関するネット上での議論は空中戦や宗教論争になりやすく、なかなか地に足の着いた議論になりにくい。本書はその領域に対する「言語化」を試みたものでもある。日本の労働人口の減少と生産性改善の必要性が叫ばれる中で、日本人同士がいがみ合い、けなし合うのはあまりにも不毛だと筆者は考える。開発現場のエンジニアが「SI 技術の基本」について共通認識と共通言語を持ち、「根拠のある議論」に基づいてお互いが学ぶべきところを学び合い、「根拠のある SI」を各自が自信を持って実践できるようになることを心から願っている。そして本書の知見が、皆様の開発現場の改善と近代化に少しでも貢献できたのであれば、望外の幸せである。

2018年1月
赤間 信幸
日本マイクロソフト株式会社 エンタープライズサービス事業本部
プリンシパルコンサルタント

VSTS/TFS Test Manager による手動テスト(打鍵テスト)の効率化

$
0
0

先日の話ですが、5 月下旬に開催された de:code 2018 に、若手エースコンサルタントの原田さんと一緒に登壇させていただきました。私事の関係で突発で穴を開けるリスクがあり、今回は登壇を見送ろうかと思っていたのですが、原田さんに協力いただけるとのことになったので、初めて共同登壇という形にさせていただきました。二人で登壇するといろいろ気付きもあり、私自身、非常によい経験になりました。(改めて原田さん、無茶ぶりに協力していただいてありがとうございました。m(_ _)m)

さて de:code 2018 では IT Modernization の重要性について説明し、そこから手動テスト(打鍵テスト)の効率化について取り上げて解説を行いました。日本の多くの SIer では未だ Excel を使った打鍵テストが行われまくっていますが、この部分は VSTS/TFS の Test Manager を利用することにより大幅な効率改善ができます。このエントリでは、セッション内容について簡単に整理しておきたいと思います。

image

[打鍵テストの効率化に対する考え方]

手動での UI 打鍵テストの効率化については、すぐさま「自動化して効率化しよう」という話が出がちですが、自動化による効率化がうまく機能する(高いコストメリットが出る)ためにはテストの繰り返し回数が多いことが必要です。代表的には以下の 2 つのようなケースに当てはまると、自動化が極めて有効に機能します。

  • かなりの構成テストが必要(構成テスト=異なる OS やデバイス、ブラウザなどで同じテストケースを何度も繰り返すこと。最近だとスマホアプリのような場合が代表的。)
  • 同一の UI や機能に対して長期保守が必要(例えば Windows OS のように一度リリースするとそのバージョンを同じ機能のまま最大 10 年間保守し続けなければならないような場合)

しかしエンプラ系の業務システムでは、上記のような条件が満たされていないことも多く、また自動化するにしてもテストケースがあまりにも多いために全部を自動化するのが現実的ではないこともよくあります。こうした背景から、エンプラ系業務システムではかなりのテストケース(特に結合テスト以降のテスト)が手作業で行われており、この傾向は今後もあまり変わらないでしょう。

[ツールによる効率化の必要性]

とはいえ、だからといって Excel を使いまくったテストは非効率的です。一般に打鍵テストは以下の手順で進みますが、特に後半の作業はツールによる効率化を図るべきところです。

  • 網羅的かつ効率的なテストケースを組み立てる
  • 組み立てたテストケースを元に、打鍵でテストを実施する
  • テスト結果を保存するために、手作業でスナップショットを取り、Excel にぺたぺた貼り付ける
  • テスト結果を集計するために、手作業で Excel に実施結果を書き込んでいく
  • バグを報告するために、手作業で Excel を使ってバグ報告を行う

実はマイクロソフトでは、手動打鍵テストを効率化するためのツール Test Manager を提供しています。それも 2010 年から。リリースから 8 年も経過するのにまるで現場に浸透していないという不遇のツールです。ネットでググってみると、驚くほどヒットしません。日本の開発現場では驚くほど活用できるのに(涙)。

image

あるお客様から聞いたところによると、実際、Excel で実施していたテストをこの Test Manager (リッチクライアント版の MTM)に置き換えたところ、テスト工数が従来の約 1/3 で済んだ、という劇的な効果が出たそうです。1/3 は極端にしても、おそらく 1/2 ぐらいにはなると思いますので、このツールを活用しない手はないでしょう。

このツールでどんなことができるのかについては、実際に動いているところを見るのが手っ取り早いです。先日の de:code 2018 ではまさにこのデモを原田さんに実施していただきました。こちらの動画の 14:25 からを見てみていただけるとよいと思います。(25 分間ぐらいのデモですが、見れば一発でわかります)

が、どうしてもビデオ見てる暇がない! というか電車の中とかなので見れない! という方のために、要点をざっくり整理すると共に、いくつか補足情報を記載しておきます。

[Test Manager のツール概要]

Test Manager は、テストケースやテスト結果データを保存するためのサーバと、打鍵テスト中に各種のデータを収集するためのクライアントから構成されます。

  • サーバ側
    VSTS (クラウドサービス)または TFS (オンプレミス)を利用します。インターネット接続などに制約がなければ VSTS を利用するのが手軽(サーバのセットアップや維持管理が不要なため)です。なお、この手動テスト支援機能はオプション機能であるため、マーケットプレイスからアドオンをインストールする必要があります。
  • クライアント側
    ブラウザプラグイン版である Test & Feedback Client と、リッチクライアント版である MTM (Microsoft Test Manager) の 2 種類があります。モダンブラウザ(Chrome や Firefox)で Web アプリをテストする場合には前者を、WinForms や WPF、IE11 でのテストを行う場合は後者を利用してください。ブラウザプラグインはこちらから入手できます。

今回のデモでは、VSTS + Test & Feedback Client (ブラウザプラグイン)の組み合わせを使って、Java で作られた Web アプリに対する打鍵テストを行っています。デモや下記の説明を見ていただくとわかりますが、このツールは Web アプリが .NET で作られていなくても利用できます

また、TFS や VSTS はソースコード管理やビルド・リリース管理などを行うツールですが、Test Manager は単体で利用することができます。つまり、TFS/VSTS でソースコード管理などを行っていなくても Test Manager による打鍵テスト効率化を行うことができます

[Test Manager でできること]

Test Manager でできることは主に以下の通りです。

  • テストケース一覧の管理(Excel からの貼り付けもできます)
  • 構成パターンの管理(Chrome, Firefox などで別々にテストする際、テスト結果を分けて取り扱えます)
  • テスト中のログ保存(ビデオによる録画や打鍵ログが取得できます)
  • バグ報告との連動(取得したログを添付する形でバグ報告を簡単に出せます)
  • テスト結果やバグの集計(テストの実施結果をグラフで集計するダッシュボードを簡単に作れます)

以下にそれぞれの概要を解説します。

[テストケース一覧の管理]

良いテストを行うには、網羅的かつ効率的なテストケースを組み立てることがまず必要ですが、この部分はツールでどうこうできるものではなく、やはり従来通り、頭を使って考える必要があります(具体的なテクニックは拙著で解説)。テストケースの組み立てはかなりの試行錯誤が必要になるため、通常は Excel を使って行います。ここは今まで通りで OK。

image

しかしどんなに Excel 大好きでも、Excel を使うのはここまでにしましょう。このテストケース一覧は、Test Manager に貼り付けて(=Test Manager に取り込んで)使います。

image

image

この作業に関していくつか Tips です(概要だけ知りたい方は読み飛ばして OK です)。

  • 一般的に業務システム開発でのテストでは、テストケース数が膨大になるため、各テストケースについての打鍵手順(操作手順や確認手順)を細かく書かないことが多いです。しかし、細かい作業手順書がある場合には、これも Test Manager に取り込むことが可能です(これをテスト手順(Test Steps)と呼びます)。詳細はビデオで解説されていますので、そちらをご覧ください。
  • テストケースを取り込む際には、① テスト計画と、② テストスイート(フォルダ)の 2 つを作る必要があります。通常、テスト計画はリリース単位(v1 リリース用とか v2 リリース用とか)、テストスイートは業務単位や要件単位に作ります。なお、後でのデータ集計を見やすくするため、テストスイートの階層数を揃えておき、番号をつけておくのがオススメです。
  • テストケースの取り込みは UI 上からコピペする方法以外に、WIT に Excel から VSTS/TFS につないでインポートする方法もあります。後者の方法を使うと、優先度やエリア情報などもまとめてインポートできるので、慣れてきたらこちらがオススメです。

[構成パターンの管理]

もし同じテストを Windows 7, 10、あるいは Chrome と FireFox など複数の構成パターンで実施する場合には、テストケースを重複登録するのではなく、構成パターンを定義してください。構成パターンを定義した上で、各テストケースに対してどの構成パターンで実施するのかを指定すると、一つのテストケースが複数のテストに分かれます。

vlcsnap-2018-06-20-10h12m21s673

vlcsnap-2018-06-20-10h12m36s120

この作業に関していくつか Tips です(概要だけ知りたい方は読み飛ばして OK です)。

  • 構成パターンを割り当てるとひとつのテストケースが複数に分裂しますが、Test Manager ではこの分裂したひとつひとつのことを “Test Point” (または単純に “Test”)と呼んでいます。テストケース×構成パターン=テストポイント(テスト実施点)、と理解するとよいでしょう。このことからわかるように、Test Manager の UI 上に表示されているのは、テストケースではなくテストポイントです。
  • 各テストを実施する人(打鍵する人=Tester)が決まっている場合には、”Assign Tester” により割り当てておくとよいです。ただし、実際には割り当てられていない人がそのテストをやっちゃうことも可能です。なので、このテスター割り当て機能は「誰がやるのかをわかりやすくしておく」だけのための機能です。
  • Test Manager では、各テストポイントを打鍵する “Tester” と、各テストケースを作成・管理する “担当者” とが別々に管理されます。Test Manager の UI 上に表示されるテストポイント一覧に表示されているのは前者、テストケースを開いたときに出てくる担当者は後者であることに注意してください。(テストケースを作った人が打鍵作業をするとは限りません)

[テスト中のログ保存]

テストの準備が整ったら、実施したいテストを選択して実行ボタンを押します。すると、ブラウザがプラグインによりテスト実行モードに切り替わります。テスト実行モードでは、各種のログを取得しながらテストの打鍵作業を行うことができます。

vlcsnap-2018-06-20-10h14m33s829

取得できる代表的なログは以下の 2 つです。

  • ① ビデオ録画
    画面全体の録画を保存できます。記録としては最強ですが、データ量が多くなることと、後から確認するのが大変なのがデメリットです。
  • ② アクションログ(打鍵ログ)
    実施した打鍵内容が一覧の形で保存されます。多くの場合、従来 Excel にぺたぺたとスナップショットを貼り付け保存していた内容はこのログで代替できます(下の画面で文字化けを起こしているのはエンコード設定の影響です;)

vlcsnap-2018-06-20-10h16m16s425

これ以外にも、手作業でスクリーンショットを保存したり、コメントを残したりすることができます。これらはテストの実施結果として、VSTS/TFS に保存されていきます。

vlcsnap-2018-06-20-10h15m31s259

この作業に関していくつか Tips です(概要だけ知りたい方は読み飛ばして OK です)。

  • アクションログに関しては、ブラウザプラグイン版とデスクトップ版で取得内容が異なります。後者の場合は画像付きではなく、完全なテキストログとしての保存になります。(が、多くの場合はこのテキストログだけでも十分で、これとビデオログの二つがあれば画像付きアクションログがなくても事足りるでしょう。)
  • アクションログの取得に関しては、クライアント側のツールとアプリの開発技術によって取れる/取れないが変化します。例えば MTM を使う場合、WinForms はアクションログが取れますが、Java Swing アプリはアクションログが(技術的な理由により)取れません。詳細はこちらを確認してください。

[バグ報告との連動]

テスト実施中にバグと思わしき内容が見つかった場合には、バグ報告を上げることができます。このバグ報告には前述のログを添付できるため、バグピンポンを防止することができます。

vlcsnap-2018-06-20-10h18m11s055

この作業に関していくつか Tips です(概要だけ知りたい方は読み飛ばして OK です)。

  • (参考)バグピンポンとは…
    テストチームから報告されたバグが、開発チーム側でうまく再現せず、テストチームと開発チームとの間で押し問答が起こる(バグ票が行ったり来たりする)こと。バグが再現せず(No Repro 問題と呼ばれます)、バグピンポンが発生する大きな原因の一つに、「テストチームがバグを見つけるまでの手順や発生状況が正しく記録されていない」(バグ報告書に書かれている再現手順が正しくない)ことがあります。この問題は、ビデオログやアクションログを活用すると激減させることができます。
  • バグ報告の作成には 2 通りあり、テスト実施中にすぐさまバグ報告を上げる方法と、テストの打鍵を一通り終えた後にバグ報告を作成する方法があります(上のスナップショットは後者の方法)。一般的に現場では前者の方法が(ラクなので)好まれることも多いのですが、本来は後者のやり方が正しいです。(バグ報告が重複していることもありますし、バグの再現手順をきちんと確立してからバグ報告を行うのが正しいため)

[テスト結果やバグの集計]

テスト結果やバグについては、簡単に集計グラフを作成したり、それらを元にしたダッシュボードを作成することができます(この作り方はビデオ見た方が早いです、33:30 あたりから)。下記のようなダッシュボードであれば、5~10 分ぐらいでさくっと作れます。

vlcsnap-2018-06-20-10h20m36s982

vlcsnap-2018-06-20-10h22m23s690

この作業に関していくつか Tips です(概要だけ知りたい方は読み飛ばして OK です)。

  • ダッシュボードに追加する Widget で似たようなものがいろいろありますが、テスト結果の集計には “Chart for Test Plans” を利用します。このウィジェットには二つの機能があり、テスト計画(テストケース)を集計する機能と、テスト結果を集計する機能があります。前者はわざわざ集計しなくてもよいことが多いでしょうから、後者をメインで利用します。
  • バグの集計に関しては、”Chart for Test Plans” ではなく “Query Results”, “Query Title”, “Chart for Work Items” などを利用します。これは、バグ情報が内部的には作業項目管理(WIT, Work Item Tracking)と呼ばれる仕組みで管理されている(テスト結果とは別に管理されている)ためです。
  • ダッシュボードなどの UI ではなく、生データを引き抜いて Excel や Power BI などで独自レポートを作ることは結構大変です。詳細レポートを PDF レポートとして引き抜くことはできるようになっているため、まずはこれを活用していただくことをお薦めします。ちなみに生データを使ったレポート作成が大変な理由は、そもそも TFS/VSTS 内でのテスト結果データの構造がかなり複雑(テストケース/テストポイント/テスト実施などのデータ構造を正しく理解しないといけない)なことが最大の理由です。データ引き抜き自体は API 経由でできますので、どうしても、という方は頑張ってみてください。:-)
  • 各テストのテスト結果は、Test Manager の UI 上から簡単に変更ができます(ボタンを押すだけで簡単に結果が変更できてしまう)が、この変更機能は主にリグレッションテストなどのためにテスト結果をリセットしたい場合に利用するものです。担当者が勝手に変更したかどうかは、前述の詳細レポートを出力してみて、テストに要した時間を確認してみるとわかります。(テストに要した時間が 00:00 だった場合には、UI 上からテスト結果を変更している)

[Test Manager 活用に関して知っておくとよいこと]

Test Manager の活用に関して、いくつか重要な点を補足します。

① テストそのものの効率化について

Excel まみれのテストを行っている場合、Test Manager を導入するだけでもかなりの改善効果が期待できますが、さらに抜本的な改善をするためには、テストのやり方そのものの見直し・改善が必要です。特に日本のエンプラ系 SI 開発現場では、以下のような問題がよく見受けられます。

  • SIer と協力会社とで、やっているテストの内容に重複がある
  • 協力会社からテストケース一覧を出してもらうものの、SIer の方で適切にレビューができない(=テストケース漏れでバグがすり抜ける)
  • そもそも単体テストの内容が不十分で、SIer に協力会社から納品されるアプリにかなりのバグが残っている

これらの問題は Test Manager などのツールの導入で解決できるものではなく、テストの考え方ややり方に対する抜本的な見直しが必要です。本エントリの範疇を超えるためここでは深掘りしませんが、本内容については拙著の第 3 章にて、テストケースの設計テクニックやツール活用方法まで含めて詳細に解説しています。こちらの情報をぜひ読んでみてください。

② ツールの費用について

Test Manager は無償ツールではなく、一人ひと月あたり $52 (日本円で \6000 弱程度)の費用がかかります(VS Ent や Test Pro のライセンスを持っている場合には無償)。また、VSTS を利用する場合、5 ユーザまでは無償ですがそれ以上になると一人ひと月あたり \500~\1000 程度の費用がかかります。この価格に関して高い! という方がいらっしゃるのですが;、個人的には、Excel で手作業でスナップショットを貼り付ける作業のために人件費をどれだけ垂れ流しているのかと考えたら激安なんじゃないかと思うのですが……どうでしょうか。っつーかテスト工数が 1/3 になった、なんていう事例もあるぐらいなので、どう考えてもツール費用なんて激安だと思うんですが;。

確かに、大人の事情で人件費は聖域だけどツール費用は一文たりとも払えないんです、というケースもあると思います。そのような場合にはせめてステップ記録ツールを使ってください。このツールは Windows OS の標準ツール(7 以降で搭載)で、アクションログのようなログデータを簡単に作れます。残念ながら集計作業やバグ報告との連携はありませんが、少なくとも Excel にスナップショットぺたぺた貼り付けるよりは圧倒的によいと思います。

なお誤解されやすいのですが、Test Manager で利用するブラウザプラグイン(Test & Feedback プラグイン)そのものは無償です。しかしこのプラグインだけだとテストケースとの連動ができないため、実態として使い物になりません。このため、Test Manager との組み合わせが必須となります。

③ 実際に試用してみたい方へ

とりあえず使ってみたい! という方は、30 日 Trial を使ってみることをオススメします。ざっくりした試用開始の手順は以下の通りです。

  • www.visualstudio.com へ行き、VSTS のアカウントを新規作成する。
  • 新規にチームプロジェクトを作成する。(一つの VSTS アカウント内に複数の開発プロジェクトを作れます。開発方法論はなんでも OK ですが、特にこだわりがなければ Agile か Scrum を指定。)
  • マーケットプレイスから Test Manager を取得する。(先に作成した VSTS アカウントへインストールする形になります。30 日間は無償。)
  • ブラウザに Test & Feedback プラグインをインストールする。(Chrome または Firefox を利用してください。)

あとは、前掲の de:code のビデオを見ながら試してみてください。

[まとめ]

de:code 2018 では、打鍵テストを取り上げてツールを使った効率化についてお話をさせていただきました。TFS/VSTS の Test Manager 機能を使っていただくと以下のようなことが可能になり、Excel ベースの打鍵テスト作業を大幅に効率化・近代化できるはずです。

    • テストケース一覧の管理(Excel からの貼り付けもできます)
    • 構成パターンの管理(Chrome, FireFox などで別々にテストする際、テスト結果を分けて取り扱えます)
    • テスト中のログ保存(ビデオによる録画や打鍵ログが取得できます)
    • バグ報告との連動(取得したログを添付する形でバグ報告を簡単に出せます)
    • テスト結果やバグの集計(テストの実施結果をグラフで集計するダッシュボードを簡単に作れます)

Test Manager に限らず、VSTS/TFS には様々な便利な機能が搭載されています。ぜひこちらの blog などを参考にして、VSTS/TFS を活用していただければと思います。

Viewing all 64 articles
Browse latest View live