はじめに / android - kotlin,java併用 / ios - swift,objective-c併用...

93

Upload: dangminh

Post on 16-Jun-2018

229 views

Category:

Documents


0 download

TRANSCRIPT

  • PYXIS

    IntelliJ

    IncomingWebHooksScalaSlack

    BeanstalkforDockerPlayFramework

    PythonScala

    GANMA!Cache

    PlayFramework

    AkkaSchedulerInstagramTumblr

    scalaDDD

    AndroidKotlin10

    2

  • Scala

    3

  • GANMA!

    ""

    2015645

    "GANMA!""MANT"

    ""GANMA!

    PYXIS

    FacebookTwitter

    Playframework+Scala/AngularJS+TypeScriptDDD

    4

  • Yahoo!JAPAN

    GANMA!

    http://ganma.jp

    Playframework+Scala/AngularJS+TypeScript/iOS-Swift/Android-ScalaDDD

    200DL2015/1212Twitter

    MANT

    http://mant.jp

    10

    LAMP/Android-Kotlin,Java/iOS-Swift,Objective-C

    5

    http://ganma.jphttp://mant.jp

  • ""

    ScalaCI

    6

  • PYXIS

    @kimutyam(1)PYXIS(2)PYXIS

    ()shardOracleMySQLGizzard(3)2

    Shardingstrategiesofteninvolvetwotechniques:partitioningandreplication.

    Wikipedia(4)

    (Partition)

    135DB130

    MySQL5.6InnoDB

    DDL

    7PYXIS

  • CREATETABLE`Sample`(`sampleId`BIGINTNOTNULLCOMMENT'ID',`date`DATENOTNULLCOMMENT'',`name`VARCHAR(255)NOTNULLCOMMENT'',`hogeId`BIGINTNOTNULLCOMMENT'HogeID',PRIMARYKEY(`sampleId`,`date`),CONSTRAINT`sample_hogeId`FOREIGNKEY(`hogeId`)REFERENCES`Hoge`(`hogeId`)ONDELETECASCADEONUPDATECASCADE)ENGINE=InnoDBDEFAULTCHARACTERSET=utf8mb4COMMENT='';

    MySQL

    (5)

    InnoDB(6)InnoDBMySQLInnoDBInnoDB

    []MySQL[]

    ((7)()RDBMSMySQL

    8PYXIS

  • Gizzard

    MySQL

    RANGE(8)Sample.dateRANGE1

    (9)

    MySQL5.6.7NDB1024MySQL5.6.78192MySQLServer

    11

    CREATETABLE`Sample`(`sampleId`BIGINTNOTNULLCOMMENT'ID',`date`DATENOTNULLCOMMENT'',`name`VARCHAR(255)NOTNULLCOMMENT'',`hogeId`BIGINTNOTNULLCOMMENT'HogeID',PRIMARYKEY(`sampleId`,`date`),)ENGINE=InnoDBDEFAULTCHARACTERSET=utf8mb4COMMENT=''PARTITIONBYRANGE(`date`)(PARTITIONp20151001VALUESLESSTHAN('2015-11-01')COMMENT='~2015/10/31'ENGINE=InnoDB)

    DDLMySQL

    1SQL

    9PYXIS

  • ALTERTABLEAdReportPARTITIONBYRANGE(`date`)(PARTITIONp20151001VALUESLESSTHAN('2015-10-01')ENGINE=InnoDB,PARTITIONp20151101VALUESLESSTHAN('2015-11-01')ENGINE=InnoDB,PARTITIONp20151201VALUESLESSTHAN('2015-12-01')ENGINE=InnoDB,PARTITIONp20160101VALUESLESSTHAN('2016-01-01')ENGINE=InnoDB,PARTITIONp20160201VALUESLESSTHAN('2016-02-01')ENGINE=InnoDB....);

    ALTERTABLEALTERTABLE

    RANGEALTERTABLEADDPARTITION

    ALTERTABLEAdReportADDPARTITION(PARTITIONp20151202VALUESLESSTHAN('2020-12-02'));

    InnoDBDDL(10)

    RANGELISTADDPARTITIONDROPPARTITION

    MAXVALUE

    CREATETABLE`Sample`(`sampleId`BIGINTNOTNULLCOMMENT'ID',`date`DATENOTNULLCOMMENT'',`name`VARCHAR(255)NOTNULLCOMMENT'',`hogeId`BIGINTNOTNULLCOMMENT'HogeID',PRIMARYKEY(`sampleId`,`date`),)ENGINE=InnoDBDEFAULTCHARACTERSET=utf8mb4COMMENT=''PARTITIONBYRANGE(`date`)(PARTITIONp20151001VALUESLESSTHAN('2015-11-01')COMMENT='~2015/10/31'ENGINE=InnoDB,PARTITIONpmaxVALUESLESSTHANMAXVALUEENGINE=InnoDB)

    ALTERTABLEAdReportREORGANIZEPARTITIONpmaxINTO(PARTITIONp20151101VALUESLESSTHAN('2015-12-01')COMMENT='~2015/11/30'ENGINE=InnoDB,PARTITIONpmaxVALUESLESSTHANMAXVALUEENGINE=InnoDB)

    ALTERTABLE

    10PYXIS

  • []

    RANGE()

    11PYXIS

  • ScalaScalikeJDBC(11)Joda-Time(12)

    RANGEVO

    packagedb.partition

    importorg.joda.time.LocalDateimportorg.joda.time.format.ISODateTimeFormat

    traitPartitionRange{valpartitionName:PartitionRangeNamevalvalue:Any}

    private[partition]caseclassPartitionRangeImpl(partitionName:PartitionRangeName,value:Any)extendsPartitionRange

    objectPartitionRange{

    /***Date1**jodaTime*/defofDateRangePerMonth(year:Int,month:Int):PartitionRange={valdate=newLocalDate(year,month,PartitionRangeConstant.BeginningOfMonthDay)

    ofDateRangePerMonth(date)}

    /***Date1*/defofDateRangePerMonth(date:LocalDate):PartitionRange={

    //require(date.getDayOfMonth==PartitionRangeConstant.BeginningOfMonthDay)

    PartitionRangeImpl(PartitionRangeName.ofDateRangePerMonth(date),date.plusMonths(1).toString(ISODateTimeFormat.date))}}

    12PYXIS

  • RANGEVO

    packagedb.partition

    importorg.joda.time.LocalDateimportorg.joda.time.format.ISODateTimeFormat

    traitPartitionRangeName{valvalue:String}

    private[partition]caseclassPartitionRangeColumnsNameImpl(value:String)extendsPartitionRangeName

    objectPartitionRangeName{privatevalpartitionNamePrefix="p"

    defapply(value:String):PartitionRangeName={PartitionRangeColumnsNameImpl(value)}

    private[partition]defofDateRangePerMonth(date:LocalDate):PartitionRangeName={

    //require(date.getDayOfMonth==PartitionRangeConstant.BeginningOfMonthDay)

    PartitionRangeColumnsNameImpl(partitionNamePrefix+date.toString(ISODateTimeFormat.basicDate))}}

    packagedb.partition

    importscalikejdbc._

    traitPartitionProviderOnMySQLextendsFindFeaturePartitionOnMySQLwithAddFeaturePartitionOnMySQLwithDropFeaturePartitionOnMySQL

    /*****trait*/traitFindFeaturePartitionOnMySQLextendsBasicPartitionHelper{

    13PYXIS

  • deffindPartitionNameBy(partitionRangeName:PartitionRangeName)(implicits:DBSession):Option[PartitionRangeName]={SQL(s"""|SELECTPARTITION_NAME|FROMINFORMATION_SCHEMA.PARTITIONS|WHERETABLE_NAME='$tableName'ANDPARTITION_NAME='${partitionRangeName.value}';""".stripMargin).map{resultSet=>

    PartitionRangeName(resultSet.string("PARTITION_NAME"))}.single().apply()}}

    traitAddFeaturePartitionOnMySQLextendsBasicPartitionHelper{

    defaddRangeColumnsPartition(range:PartitionRange)(implicits:DBSession):Int={SQL(s"""|ALTERTABLE${toBackQuoteString(tableName)}ADDPARTITION(|PARTITION${range.partitionName.value}VALUESLESSTHAN(?)|)""".stripMargin).bind(range.value).update().apply()}}

    traitDropFeaturePartitionOnMySQLextendsBasicPartitionHelper{

    defdropRangeColumnsPartition(partitionRangeName:PartitionRangeName)(implicits:DBSession):Int={dropPartition(partitionRangeName.value)}

    defdropPartition(partitionName:String)(implicits:DBSession):Int={SQL(s"""|ALTERTABLE${toBackQuoteString(tableName)}DROPPARTITION$partitionName""".stripMargin).update().apply()}}

    traitBasicPartitionHelper{protecteddeftableName:String

    protecteddeftoBackQuoteString(self:String):String=s"`$self`"

    14PYXIS

  • }

    packagedb.partition

    objectPartitionRangeConstant{/****/valBeginningOfMonthDay=1}

    packagedb.partition.sample

    importdb.partition.PartitionProviderOnMySQL

    classSamplePartitionProviderextendsPartitionProviderOnMySQL{protecteddeftableName="Sample"}

    15PYXIS

  • packagebatch

    importorg.joda.time.LocalDateimportdb.partition.PartitionRangeimportdb.partition.sample.SamplePartitionProviderimportscalikejdbc.{AutoSession,DBSession}

    objectSamplePartitionAddBatchextendsApp{implicitvals:DBSession=AutoSession

    valnowDate=LocalDate.nowvalpartitionRange=PartitionRange.ofDateRangePerMonth(nowDate.getYear,nowDate.getMonthOfYear)

    valprovider=newSamplePartitionProvider

    if(provider.findPartitionNameBy(partitionRange.partitionName).isEmpty){provider.addRangeColumnsPartition(partitionRange)}}

    16PYXIS

  • packagebatch

    importorg.joda.time.LocalDateimportdb.partition.PartitionRangeimportdb.partition.sample.SamplePartitionProviderimportscalikejdbc.{AutoSession,DBSession}

    objectSamplePartitionDeleteBatchextendsApp{implicitvals:DBSession=AutoSession

    valtargetDate=LocalDate.now.minusYears(1)

    valpartitionRange=PartitionRange.ofDateRangePerMonth(targetDate.getYear,targetDate.getMonthOfYear)

    valprovider=newSamplePartitionProvider

    provider.findPartitionNameBy(partitionRange.partitionName).map{_=>provider.dropRangeColumnsPartition(partitionRange.partitionName)}}

    1Twitterhttps://twitter.com/kimutyam2PYXIShttp://pyxis-social.com/PYXIS3Twitterhttps://github.com/twitter/gizzard4https://ja.wikipedia.org/wiki/%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%B35https://dev.mysql.com/doc/refman/5.6/ja/partitioning-pruning.html6https://dev.mysql.com/doc/refman/5.6/ja/partitioning-limitations-storage-engines.html7()MySQLhttps://songmu.github.io/slides/fk-night/#08https://dev.mysql.com/doc/refman/5.6/ja/partitioning-range.html9https://dev.mysql.com/doc/refman/5.6/ja/partitioning-limitations.html10https://dev.mysql.com/doc/refman/5.6/ja/online-ddl-partitioning.html11http://scalikejdbc.org/12http://www.joda.org/joda-time/

    17PYXIS

    https://twitter.com/kimutyamhttp://pyxis-social.com/https://github.com/twitter/gizzardhttps://ja.wikipedia.org/wiki/%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%B3https://dev.mysql.com/doc/refman/5.6/ja/partitioning-pruning.htmlhttps://dev.mysql.com/doc/refman/5.6/ja/partitioning-limitations-storage-engines.htmlhttps://songmu.github.io/slides/fk-night/#0https://dev.mysql.com/doc/refman/5.6/ja/partitioning-range.htmlhttps://dev.mysql.com/doc/refman/5.6/ja/partitioning-limitations.htmlhttps://dev.mysql.com/doc/refman/5.6/ja/online-ddl-partitioning.htmlhttp://scalikejdbc.org/http://www.joda.org/joda-time/

  • IntelliJ

    ScalaIntelliJIDEAIDEIntelliJIntelliJIntelliJIntelliJIntelliJGitScala

    ScalaOSMacIntelliJ14.1.5

    Command+Alt+

    Ctrl+G

    ()

    Command+/

    Alt+Enter

    IntelliJ

    18IntelliJ

  • Command+Shift+V

    IntelliJ

    Alt+Shift+

    Command+Shift+F

    Shift2

    Command+F4

    Command+B

    Command+D

    Command+Y

    Command+Alt+L

    Command+E

    19IntelliJ

  • Command+Alt+B

    Ctrl+H

    IntelliJHierarchy

    Alt+Shift+H

    IntelliJHierarchy

    Command+WCommand+Shift+W

    Command+G

    ()

    Command+Shift+/

    Command+Shift+

    /

    Command+1

    /

    Command+7

    Command+

    20IntelliJ

  • ifwhilefor

    Command+Alt+T

    forif

    Shift+Enter

    Command+Alt+N

    []

    Command+Alt+F7

    Command+Alt+M

    Command+Alt+F

    UML

    Command+Alt+U

    UML

    UML

    Command+Alt+Shift+T

    21IntelliJ

  • Command+

    Alt+

    ->[Annotate]

    Git

    [Annotate]

    ->[Rollback]

    Diff

    ->[Git]->[ShowHistory]->

    Git

    ->[Local

    22IntelliJ

  • History]->[ShowHistory]

    IntelliJIDEA-http://qiita.com/yoppe/items/f7cbeb825c071691d3f2IntelliJIDEAGit-http://qiita.com/yoppe/items/fd03607d4d4f191d32dd

    23IntelliJ

    http://qiita.com/yoppe/items/f7cbeb825c071691d3f2http://qiita.com/yoppe/items/fd03607d4d4f191d32dd

  • IncomingWebHooksScalaSlack

    RedmineBitbucketWebHookWebHook=ScalaSlackIncomingWebHooks

    IncomingWebHooks

    IncomingWebHooksJsonURLSlackSlack

    Slack

    Slack

    1. IncomingWebHookshttps://[].slack.com/services/new/incoming-webhook

    2. IncomingWebHookshttps://[].slack.com/services#serviceIncomingWebHookswebhookURL

    Slack

    Slack

    curl-XPOST--data-urlencode'payload={"text":"helloworld"}'https://(webhookurl)

    ScalaSlack

    SlackScala@Slacksbt

    URLJsonbuild.sbthttpJson

    build.sbt

    24IncomingWebHooksScalaSlack

    https://[].slack.com/services/new/incoming-webhookhttps://[].slack.com/services#service

  • libraryDependencies+="com.typesafe.play"%"play-ws_2.11"%"2.4.3"libraryDependencies+="com.typesafe.play"%"play-json_2.11"%"2.4.3"

    SlackJson

    importplay.api.libs.json._

    /****@paramlink_names@*@paramattachments*/caseclassMessage(link_names:Int,attachments:Seq[Attachment])

    /****@paramfallback*@paramcolor(?)good,warning,danger*@paramtitle*@paramtext*/caseclassAttachment(fallback:String,color:String,title:String,text:String)

    /***Json*/objectMessage{implicitvalattachmentWrite=Json.writes[Attachment]implicitvalmessageWrite=Json.writes[Message]}

    link_namescolorEnum

    25IncomingWebHooksScalaSlack

  • /***@MentionEnum*/sealedabstractclassMention(valvalue:Int)objectMention{caseobjectTrueextendsMention(1)caseobjectFalseextendsMention(0)}

    /***Enum*/sealedabstractclassColor(valvalue:String)objectColor{caseobjectGoodextendsColor("good")caseobjectWarningextendsColor("warning")caseobjectDangerextendsColor("danger")}

    URL

    26IncomingWebHooksScalaSlack

  • Slack

    SlackwebHookURLSlack2

    importplay.api.libs.json._importplay.api.libs.ws._importplay.api.libs.ws.ning.NingWSClientimportscala.concurrent.duration._importscala.concurrent.{Await,Future}

    //SlackvalwebHookURL="https://hooks.slack.com/services/????"

    valmessage=Message(link_names=Mention.True.value,attachments=Seq(Attachment(fallback="@hoge",color=Color.Good.value,title="",text="@hoge")))

    valdata=Json.toJson(message)

    defusing[A](client:NingWSClient)(f:NingWSClient=>A):A={try{f(client)

    }finally{client.close()}}

    using[WSResponse](NingWSClient()){client=>valfutureResponse:Future[WSResponse]=client.url(webHookURL).post(data)Await.result(futureResponse,Duration.Inf)}

    27IncomingWebHooksScalaSlack

  • IncomingWebHooksSlack

    URL

    https://api.slack.com/incoming-webhookshttps://api.slack.com/docs/formattinghttps://api.slack.com/docs/attachments

    28IncomingWebHooksScalaSlack

    https://api.slack.com/incoming-webhookshttps://api.slack.com/docs/formattinghttps://api.slack.com/docs/attachments

  • BeanstalkforDockerPlayFramework

    BeanstalkforDockerPlayFramework

    Docker1.8AWSCloudformation

    sfn1

    sparkle_formation2

    AWSElasticBeanstalkforDocker()3

    DockerHub(EC2build)PlayFramework2.4

    key_pairsample_project~/.sshebssh

    29BeanstalkforDockerPlayFramework

  • 1. BeanstalkwithCloudformation2. PlayFramework3. Docker4. Docker5. (awsebcli)6. Beanstalk

    CloudformationBeanstalk

    CloudformationBeanstalkforDocker

    1.

    jsonsfnsparkle_formationgemRuby

    sparkle_formation

    ##VPC#InternetGateway

    30BeanstalkforDockerPlayFramework

  • #RouteTable#Subnet#SecurityGroup#BeanstalkforDocker

    SparkleFormation.new(:SampleProjectTemplate)doset!('AWSTemplateFormatVersion','2010-09-09')description"SampleProject"

    parametersdoprojectdodescription'ProjectName'type'String'default'sample-project'endend##########################VPC#########################resources(:Vpc)dotype'AWS::EC2::VPC'propertiesdocidr_block'10.0.0.0/16'endend

    ##########################InternetGateway#########################resources(:InternetGateway)dotype'AWS::EC2::InternetGateway'propertiesdotags_array(->{

    key'Project'valueref!(:Project)})

    endend

    resources(:AttachGateway)dotype'AWS::EC2::VPCGatewayAttachment'propertiesdointernet_gateway_idref!(:internet_gateway)vpc_idref!(:vpc)endend

    31BeanstalkforDockerPlayFramework

  • ##########################RouteTable#########################resources(:PublicRouteTable)dotype'AWS::EC2::RouteTable'propertiesdovpc_idref!(:Vpc)endend

    ##RouteforInternetresources(:PublicRoute)dotype'AWS::EC2::Route'propertiesdodestination_cidr_block'0.0.0.0/0'gateway_idref!(:InternetGateway)route_table_idref!(:PublicRouteTable)endend

    ##########################Subnet#########################resources(:PublicSubnet)dotype'AWS::EC2::Subnet'propertiesdoavailability_zone'ap-northeast-1b'cidr_block'10.0.0.0/24'map_public_ip_on_launch'true'vpc_idref!(:Vpc)endend

    resources(:PublicSubnetRouteTableAssociation)dotype'AWS::EC2::SubnetRouteTableAssociation'propertiesdoroute_table_idref!(:PublicRouteTable)subnet_idref!(:PublicSubnet)endend

    ##########################SecurityGroup#########################resources(:PublicSecurityGroup)dotype'AWS::EC2::SecurityGroup'propertiesdogroup_description'PublicSecurityGroup'

    32BeanstalkforDockerPlayFramework

  • vpc_idref!(:Vpc)endend

    resources(:PublicSecurityGroupIngress1)dotype'AWS::EC2::SecurityGroupIngress'propertiesdosource_security_group_idref!(:PublicSecurityGroup)from_port0to_port65535ip_protocol-1group_idref!(:PublicSecurityGroup)endend

    resources(:PublicSecurityGroupEgress1)dotype'AWS::EC2::SecurityGroupEgress'propertiesdocidr_ip'0.0.0.0/0'from_port0to_port65535ip_protocol-1group_idref!(:PublicSecurityGroup)endend

    resources(:ServerRole)dotype'AWS::IAM::Role'propertiesdoassume_role_policy_documentdostatement_array(->{

    effect'Allow'principaldoservice_array(

    'ec2.amazonaws.com')

    endaction['sts:AssumeRole']})

    endpath'/'endend

    resources(:ServerPolicy)dotype'AWS::IAM::Policy'depends_on"ServerRole"

    33BeanstalkforDockerPlayFramework

  • propertiesdopolicy_name'ServerRole'policy_documentdostatement_array(->{

    effect'Allow'not_action'iam:*'resource'*'})

    endroles_array(

    ref!(:ServerRole))

    endendresources(:ServerInstanceProfile)dotype'AWS::IAM::InstanceProfile'depends_on"ServerRole"propertiesdopath'/'roles_array(

    ref!(:ServerRole))

    endend

    resources(:SampleProjectApplicatioin)dotype'AWS::ElasticBeanstalk::Application'propertiesdodescription'SampleProjectApplication'application_name'SampleProject'endend

    resources(:SampleProjectApplicatioinEnvironment)dotype'AWS::ElasticBeanstalk::Environment'depends_on["SampleProjectApplicatioin","ServerRole"]propertiesdoapplication_nameref!(:SampleProjectApplicatioin)description"SampleProjectforStaging"solution_stack_name'64bitAmazonLinux2015.03v2.0.2runningDocker1.7.1'environment_name'SampleProjectStaging'CNAMEPrefix'sample-projet-staging'tierdoname'WebServer'type'Standard'end

    34BeanstalkforDockerPlayFramework

  • option_settings_array(->{

    namespace'aws:autoscaling:launchconfiguration'option_name'SSHSourceRestriction'value"tcp,22,22,113.34.78.168/32"},->{

    namespace'aws:autoscaling:launchconfiguration'option_name'SecurityGroups'valueref!(:PublicSecurityGroup)},->{

    namespace'aws:autoscaling:launchconfiguration'option_name'EC2KeyName'value'sample_project'},->{

    namespace'aws:ec2:vpc'option_name'VPCId'valueref!(:Vpc)},->{

    namespace'aws:ec2:vpc'option_name'AssociatePublicIpAddress'value"true"},->{

    namespace'aws:ec2:vpc'option_name'Subnets'valueref!(:PublicSubnet)},->{

    namespace'aws:ec2:vpc'option_name'ELBSubnets'valueref!(:PublicSubnet)},->{

    namespace'aws:autoscaling:launchconfiguration'option_name'InstanceType'value't2.micro'})

    endendend

    35BeanstalkforDockerPlayFramework

  • 2.stack

    sfnstack

    $bundleexecsfncreatesample-project-Beanstalk-btemplates/-ftemplates/Beanstalks.json-P

    [Sfn]:SparkleFormation:create[Sfn]:->Name:sample-project[Sfn]:Stackruntimeparameters:[Sfn]:Project[sample-project]:

    [Sfn]:EventsforStack:sample-projectTimeResourceLogicalIdResourceStatusResourceStatusReason

    2015-11-2703:01:36UTCsample-projectCREATE_IN_PROGRESSUserInitiated

    2015-11-2703:01:40UTCVpcCREATE_IN_PROGRESS2015-11-2703:01:40UTCInternetGatewayCREATE_IN_PROGRESS2015-11-2703:01:40UTCSampleProjectApplicatioinCREATE_IN_PROGRESS

    (snip)

    2015-11-2703:04:08UTCServerInstanceProfileCREATE_COMPLETE2015-11-2703:08:32UTCSampleProjectApplicatioinEnvironmentCREATE_COMPLETE2015-11-2703:08:34UTCsample-projectCREATE_COMPLETE[Sfn]:Stackcreatecomplete:SUCCESS[Sfn]:Stackdescriptionofsample-project:

    [Sfn]:Outputsforstack:sample-project[Sfn]:Nooutputsfound

    3.Beanstalk

    BeanstalkURLcname_prefixsample-projet-staginghttp://sample-projet-staging.elasticbeanstalk.com/URL

    36BeanstalkforDockerPlayFramework

    http://sample-projet-staging.elasticbeanstalk.com/

  • 1.

    PlayFramework

    #$./activatornewsample-projectplay-scala

    $cdsample-project/

    #$vimapp/views/main.scala.html@(title:String)(content:Html)

    @title

    2016ScalaMaturiSampleProject

    #$./activatorrun

    2.buildjar

    SBTassemblyplugin4jar5

    37BeanstalkforDockerPlayFramework

  • $vimproject/plugins.sbt

    #addSbtPlugin("com.eed3si9n"%"sbt-assembly"%"0.14.1")

    $vimbuild.sbt

    #importAssemblyKeys._

    assemblySettings

    mainClassinassembly:=Some("play.core.server.NettyServer")

    fullClasspathinassembly+=Attributed.blank(PlayKeys.playPackageAssets.value)

    #java.lang.RuntimeException:deduplicate:differentfilecontentsfoundinthe#following:mergeStrategyinassemblyMergeStrategy.firstcasex=>old(x)}}

    #build$./activatorassembly[info]Loadingprojectdefinitionfrom/Users/t_saeki/Develop/sample-project/project

    [info]Setcurrentprojecttosample-project(inbuildfile:/Users/t_saeki/Develop/sample-project/)

    [info]Includingfromcache:config-1.3.0.jar[info]Includingfromcache:bonecp-0.8.0.RELEASE.jar

    (snip)

    [info]Assemblyuptodate:/Users/t_saeki/Develop/sample-project/target/scala-2.11/sample-project-assembly-1.0-SNAPSHOT.jar[success]Totaltime:6s,completed2015/11/2618:24:05

    ##http://localhost:9000$java-jartarget/scala-2.11/sample-project-assembly-1.0-SNAPSHOT.jarNettyServer.mainisdeprecated.PleasestartyourPlayserverwiththe${ProdServerStart.getClass.getName}.main.[info]-play.api.Play-Applicationstarted(Prod)

    [info]-play.core.server.NettyServer-ListeningforHTTPon/0:0:0:0:0:0:0:0:9000

    38BeanstalkforDockerPlayFramework

  • Docker

    Docker

    1.Docker

    6docker-machinedocker-compose

    2.Dockerfile

    Dockerfile7java8

    $vimDockerfile

    FROMjava:8

    RUNmkdir/opt/sample-project

    COPYtarget/scala-2.11/sample-project-assembly-1.0-SNAPSHOT.jar/opt/sample-project/

    EXPOSE9000CMD["java","-jar","/opt/sample-project/sample-project-assembly-1.0-SNAPSHOT.jar"]

    39BeanstalkforDockerPlayFramework

  • 3.Dockerbuildrun

    DockerbuildDockerDocker9

    $dockerbuild-ttsaeki/sample-project.

    SendingbuildcontexttoDockerdaemon162MBStep0:FROMjava:8--->36621d4ab8e3Step1:RUNmkdir/opt/sample-project--->Usingcache--->b5e800d8653f

    Step2:COPYtarget/scala-2.11/sample-project-assembly-1.0-SNAPSHOT.jar/opt/sample-project/--->Usingcache

    --->74ec351a9034Step3:EXPOSE9000--->Usingcache--->fd1947bdc2e5

    Step4:CMDjava-jar/opt/sample-project/sample-project-assembly-1.0-SNAPSHOT.jar--->Usingcache

    --->0a8f13d586ceSuccessfullybuilt0a8f13d586ce

    ##http://192.168.99.100:9000$Dockerrun-it--rm-p9000:9000tsaeki/sample-projectNettyServer.mainisdeprecated.PleasestartyourPlayserverwiththe${ProdServerStart.getClass.getName}.main.[info]-play.api.Play-Applicationstarted(Prod)

    [info]-play.core.server.NettyServer-ListeningforHTTPon/0:0:0:0:0:0:0:0:9000

    40BeanstalkforDockerPlayFramework

  • 4.Dockerrun.aws.json

    BeanstalkDockerDockerrun.aws.json10

    {

    "AWSEBDockerrunVersion":"1","Image":{"Name":"tsaeki/sample-project","Update":"true"},

    "Ports":[{

    "ContainerPort":"9000"}],

    "Volumes":[],"Logging":""}

    Beanstalk

    1.EBCLIforMac

    EBCLI1112brew2.xpip

    $sudopipinstallawsebcli

    2.EBCLI

    ebinitRegionApplication

    41BeanstalkforDockerPlayFramework

  • $ebinit

    Selectadefaultregion

    1)us-east-1:USEast(N.Virginia)2)us-west-1:USWest(N.California)3)us-west-2:USWest(Oregon)4)eu-west-1:EU(Ireland)5)eu-central-1:EU(Frankfurt)6)ap-southeast-1:AsiaPacific(Singapore)7)ap-southeast-2:AsiaPacific(Sydney)8)ap-northeast-1:AsiaPacific(Tokyo)9)sa-east-1:SouthAmerica(SaoPaulo)10)cn-north-1:China(Beijing)(defaultis3):8

    Selectanapplicationtouse

    1)SampleProject2)[CreatenewApplication](defaultis2):1

    ItappearsyouareusingDocker.Isthiscorrect?(y/n):y

    Selectaplatformversion.

    1)Docker1.7.12)Docker1.6.2(defaultis1):1DoyouwanttosetupSSHforyourinstances?(y/n):y

    Selectakeypair.

    1)sample_project2)[CreatenewKeyPair](defaultis2):sample_project

    ebinit-i

    42BeanstalkforDockerPlayFramework

  • 3.

    #Environmentlist$eblist*SampleProjectStaging

    $ebdeploySampleProjectStaging

    Creatingapplicationversionarchive"app-151127_133042".Uploading:[##################################################]100%Done...INFO:Environmentupdateisstarting.INFO:TheenvironmentdoesnothaveanIAMinstanceprofileassociatedwithit.ToimprovedeploymentspeedpleaseassociateanIAMinstanceprofilewiththeenvironment.INFO:Deployingnewversiontoinstance(s).

    INFO:Successfullypulledjava:8INFO:Successfullybuiltaws_Beanstalk/staging-appINFO:Dockercontainerd72d6432f5f8isrunningaws_Beanstalk/current-app.INFO:NewapplicationversionwasdeployedtorunningEC2instances.INFO:Environmentupdatecompletedsuccessfully.

    #SampleProjectStaging(*)

    4.

    http://sample-projet-staging.elasticbeanstalk.com/

    (mackerel)

    mackerel13mackerel

    mackerel.ebextensions14Dockerjvmhttpjolokia15mackerelapikey

    1.jolokia

    DockerJVMjolokiahttpJVM16

    #jolokiajar$curl-Ohttps://repo1.maven.org/maven2/org/jolokia/jolokia-jvm/1.3.2/jolokia-jvm-1.3.2-agent.jar

    43BeanstalkforDockerPlayFramework

    http://sample-projet-staging.elasticbeanstalk.com/

  • #jarjolokiaagent$vimDockerfile

    FROMjava:8

    RUNmkdir/opt/sample-project

    COPYtarget/scala-2.11/sample-project-assembly-1.0-SNAPSHOT.jar/opt/sample-project/

    #jarCOPYjolokia-jvm-1.3.2-agent.jar/opt/sample-project/

    #8778EXPOSE90008778

    #"-javaagent:/opt/sample-project/jolokia-jvm-1.3.2-agent.jar=port=8778,host=0.0#.0.0"CMD["java","-javaagent:/opt/sample-project/jolokia-jvm-1.3.2-agent.jar=port=8778,host=0.0.0.0","-jar","/opt/sample-project/sample-project-assembly-1.0-SNAPSHOT.jar"]

    #Dockerbuild,run$Dockerbuild-ttsaeki/sample-project.

    #8778-p8778:8778$Dockerrun-it--rm-p9000:9000-p8778:8778tsaseki/sample-project

    #$curl-shttp://192.168.99.100:8778/jolokia/|jq.{

    "request":{"type":"version"},

    "value":{"agent":"1.3.2","protocol":"7.2","config":{"maxDepth":"15","discoveryEnabled":"true","maxCollectionSize":"0","agentId":"172.17.0.3-1-5e2de80c-jvm","debug":"false","agentType":"jvm","historyMaxEntries":"10","agentContext":"/jolokia","maxObjects":"0","debugMaxEntries":"100"},

    "info":{}},

    "timestamp":1448603006,

    44BeanstalkforDockerPlayFramework

  • "status":200}

    2..ebextensionsmackerelplugin

    $mkdir.ebextensions

    $vim.ebextensions/01_install_mackerel.configfiles:/etc/mackerel-agent/mackerel-agent.conf:

    mode:"00644"owner:rootgroup:rootencoding:plaincontent:|

    apikey=""include="/etc/mackerel-agent/conf.d/*.conf"[plugin.metrics.Docker]

    command="/usr/local/bin/mackerel-plugin-Docker"

    #IP/opt/elasticBeanstalk/hooks/appdeploy/post/99_setup-jolokia-config-for-mackerel.sh:

    mode:"00755"owner:rootgroup:rootencoding:plaincontent:|

    #!/bin/sh#setupjolokiamonotoringDocker_IP=$(dockerinspect--format'{{.NetworkSettings.IPAddress}}'$(dockerps-q-l))MACKEREL_HOSTID=$(cat/var/lib/mackerel-agent/id)cat>/etc/mackerel-agent/conf.d/jolokia.conf

  • require'mackerel/client'require'socket'require'uri'require'net/http'require'json'

    MACKEREL_KEY='etEwwFKkZJW6goWf8Tqmx7awjz5aPucZC46xQm92WDHH'

    monitoring_ip=ARGV[0]host_id=ARGV[1]base_url="https://mackerel.io//api/v0/tsdb"end_time=Time.now

    uri=URI.parse("http://#{monitoring_ip}:8778/jolokia/read/java.lang:type=Memory/HeapMemoryUsage")get_json_memory=Net::HTTP.get(uri)get_heep_memory=JSON.load(get_json_memory)

    metrics=[

    {'hostId'=>host_id,'name'=>"HeapMemoryUsage.init",'time'=>end_time.to_i,'value'=>get_heep_memory['value']['init']},{'hostId'=>host_id,'name'=>"HeapMemoryUsage.committed",'time'=>end_time.to_i,'value'=>get_heep_memory['value']['committed']},{'hostId'=>host_id,'name'=>"HeapMemoryUsage.max",'time'=>end_time.to_i,'value'=>get_heep_memory['value']['max']},{'hostId'=>host_id,'name'=>"HeapMemoryUsage.used",'time'=>end_time.to_i,'value'=>get_heep_memory['value']['used']},]

    @mackerel=Mackerel::Client.new(:mackerel_api_key=>MACKEREL_KEY)[email protected]_metrics(metrics)

    commands:

    00_enable_sudo:command:sed-i's/requiretty/!requiretty/'/etc/sudoers01_setup_yumrepo_for_mackerel:command:curl-fsSLhttps://mackerel.io/assets/files/scripts/setup-yum.sh|sh

    02_install_mackerel_rpm_packages:command:yuminstall-ymackerel-agentmackerel-agent-plugins03_install_mackerel_gem_packages:command:/usr/bin/geminstallmackerel-client04_create_mackerel_dir:command:mkdir-p/etc/mackerel-agent/conf.d

    container_commands:start_mackerel_agent:

    command:"/etc/init.d/mackerel-agentrestart"

    46BeanstalkforDockerPlayFramework

  • 3..ebextentions

    $ebdeploySampleProjectStaging$ebdeploySampleProjectStaging

    Creatingapplicationversionarchive"app-151127_162030".Uploading:[##################################################]100%Done...INFO:Environmentupdateisstarting.INFO:Deployingnewversiontoinstance(s).

    INFO:Successfullypulledjava:8INFO:Successfullybuiltaws_Beanstalk/staging-app

    INFO:Dockercontainer1c4d16bd9b12isrunningaws_Beanstalk/current-app.INFO:NewapplicationversionwasdeployedtorunningEC2instances.INFO:Environmentupdatecompletedsuccessfully.

    4.mackerel

    01_prefix/opt/elasticBeanstalk/hooks/appdeploy/post/

    DockerBeanstalkmackerelBeanstalkDocker

    1https://github.com/sparkleformation/sfn

    47BeanstalkforDockerPlayFramework

    https://github.com/sparkleformation/sfn

  • 2https://github.com/sparkleformation/sparkle_formation3http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/docker-singlecontainer-deploy.html4https://github.com/sbt/sbt-assembly5https://www.playframework.com/documentation/ja/2.3.x/ProductionDist6https://docs.docker.com/v1.8/installation/mac/7https://docs.docker.com/v1.8/reference/builder/8https://hub.docker.com/_/java/9https://docs.docker.com/v1.8/reference/commandline/cli/10

    http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/create_deploy_docker_image.html#create_deploy_docker_image_dockerrun11http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/eb-cli3.html12http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/eb-cli3-install.html13https://mackerel.io/ja/14http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/ebextensions.html15https://jolokia.org/16Play2JVMJolokiahttp://blog.cloudpack.jp/2014/11/20/monitor-jvm-running-play-framework-2-with-jolokia/

    48BeanstalkforDockerPlayFramework

    https://github.com/sparkleformation/sparkle_formationhttp://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/docker-singlecontainer-deploy.htmlhttps://github.com/sbt/sbt-assemblyhttps://www.playframework.com/documentation/ja/2.3.x/ProductionDisthttps://docs.docker.com/v1.8/installation/mac/https://docs.docker.com/v1.8/reference/builder/https://hub.docker.com/_/java/https://docs.docker.com/v1.8/reference/commandline/cli/http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/create_deploy_docker_image.html#create_deploy_docker_image_dockerrunhttp://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/eb-cli3.htmlhttp://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/eb-cli3-install.htmlhttps://mackerel.io/ja/http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/ebextensions.htmlhttps://jolokia.org/http://blog.cloudpack.jp/2014/11/20/monitor-jvm-running-play-framework-2-with-jolokia/

  • PythonScala

    2015PythonWebPYXISForFacebookScalaScalaScala

    3

    (@OE_uia)ScalaScala&PlayScala&Play&(DDD)

    (@OE_uia)Scala

    @OE_uia1ScalaScalasbtspecs2Scala

    1. 2. ()REPL3. 4. @OE_uia

    PythonScala

    PythonScala

    ScalaPythonScalavalval

    Python

    49PythonScala

  • fromabcimportABCMeta,abstractmethodclassAbstractClass(object):__metaclass__=ABCMeta#

    @abstractmethod#defsample_method(self):print"abstract"

    @abstractmethod

    abstractclassAbstractClass(){defsampleMethod():Unit={println("abstract")}}

    Scalaabstractclass

    mapPython

    >>>result=range(0,10)>>>result=map(lambdax:x+1,result)>>>result[1,2,3,4,5,6,7,8,9,10]

    Scala

    scala>(0until10).map(f=>f+1).toListres4:List[Int]=List(1,2,3,4,5,6,7,8,9,10)

    Scala

    Scala

    50PythonScala

  • Scala

    Scala

    Scala&Play

    ScalaScala&PlayWebPlayFrameworkSpecs2

    ScrumScrumPOPOSLSLPOSL

    1.

    2. POSL

    1. 2. 3. 4. 5. 6.

    51PythonScala

  • SQL

    //defindex=Action{implicitvalconnection=DB.getConnection()valsql=SQL("""SELECTMessage.id,detail,emailFROMUserJOINMessage|ONUser.id=user_idORDERBYMessage.idASC""".stripMargin)

    valuserSeq=sql().map(row=>row[String]("detail")->row[String]("email")).toSeq

    Ok(views.html.index(userSeq))}

    PlayFrameworkSpecs2Web

    Scala&Play&(DDD)

    2

    scala-dddbase1

    52PythonScala

  • 1. 2.

    3. 2.

    1. 2. 3. 4.

    1.

    PO

    PO

    or

    53PythonScala

  • 2.

    ID

    3.

    2resolveBy

    54PythonScala

  • 4.

    /****id:ID*message:*email:*dateTime:*/caseclassPostingID(value:String)extendsIdentifier[String]

    traitPostingextendsEntity[PostingID]{

    validentifier:PostingIDvalmessage:Stringvalemail:StringvaldateTime:String

    }

    /****email:*password:*createPosting:*/

    caseclassLoginUserEmail(value:String)extendsIdentifier[String]

    traitLoginUserextendsEntity[LoginUserEmail]{

    validentifier:LoginUserEmailvalpassword:String

    }

    55PythonScala

  • //private[loginUser]caseclassLoginUserImp(identifier:LoginUserEmail,password:String)extendsLoginUser{

    defcreatePostingEntity(message:String):Posting={Posting(PostingID(java.util.UUID.randomUUID.toString),message,identifier.value,

    DateTime.now.toString)}}

    /***AbstractRepository*store(entity:E):*resolveBy(identifier:ID):*/abstractclassAbstractRepositoryOnAnorm[ID

  • }

    /***LoginUserRepositoryOnAnorm*/classLoginUserRepositoryOnAnormextendsAbstractRepositoryOnAnorm[LoginUserEmail,LoginUser]

    3

    Scala

    1

    DDDPO

    57PythonScala

  • Scala

    3Scala

    ScalaScala

    58PythonScala

  • GANMA!Cache

    GANMA!MemcacheCacheDDDDI

    CacheCache

    CacheCache

    GANMA!Cache3

    CacheCacheCache

    GANMA!(DDD)DDD"Model""Controller"

    CacheDBCache

    MethodCacheCacheCache

    //classCatsTable{defget(catName:String):Option[Row]=???//1

    59GANMA!Cache

  • deflist(offset:Int=0):Seq[Row]=???

    defstore(cat:RawData):Unit=???

    //}

    //CacheobjectCache{

    defmakeKey(tag:String,args:Any*):String=???

    defgetOrElseUpdate[A:ClassTag](key:String,op:=>A,expire:Int=300):A=???

    defclear(key:String):Unit=???

    //}

    //CacheclassCachedCatsTableextendsCatsTable{

    privatevalclassName=getClass.getName

    overridedefget(catName:String):Option[Row]={valkey=Cache.makeKey(className+".get",catName)Cache.getOrElseUpdate(key,super.get(catName))}

    overridedeflist(offset:Int=0):Seq[Row]={offsetmatch{case0=>//2valkey=Cache.makeKey(className+".list")Cache.getOrElseUpdate(key,super.list(offset))

    case_=>super.list(offset)}}

    overridedefstore(cat:RawData)={super.store(cat)clearCache(cat)}

    privatedefclearCache(cat:RawData):Unit={//3Cache.makeKey(className+".get",cat.name)Cache.makeKey(className+".list")}}

    60GANMA!Cache

  • 1-Future2-offset=0Cache3-MemcacheRedisHash

    CacheDDDEntityIDRepositoryCache

    GANMA!

    1. DI(GANMA!http://qiita.com/takezoux2@github/items/a2b607cdfedd21974687)

    2. DI1HTTP3. Cache

    4.

    61GANMA!Cache

    http://qiita.com/takezoux2@github/items/a2b607cdfedd21974687

  • Cache

    classInstanceCachedCatsRepositoryextendsCatsRepository{

    importscala.collection._importscala.collection.convert.decorateAsScala._

    privateobjectcache{

    valget:concurrent.Map[String,Option[Cat]]=newConcurrentHashMap[String,Option[Cat]]().asScalavallist:concurrent.Map[Int,List[Cat]]=newConcurrentHashMap[Int,List[Cat]].asScala

    defclear():Unit={get.clear()list.clear()}}

    overridedefget(catName:String):Option[Cat]=cache.get.getOrElseUpdate(catName,super.get(catName))

    overridedeflist(offset:Int=0):List[Cat]=cache.list.getOrElseUpdate(offset,super.list(offset))

    overridedefstore(cat:Cat):Unit={super.store(cat)cache.clear()}}

    62GANMA!Cache

  • DI

    traitCatMomictureServiceDepends{implicitlazyvalcatRepository=newInstanceCachedCatsRepository}

    classDomainInjectorextendsCatMomictureServiceDepends

    classCatMomictureService(implicitdepends:CatMomictureServiceDepends){

    importdepends._

    defmomi(catName:String):Unit={catRepository.get(catName)foreachdoMomicture}

    privatedefdoMomicture(cat:Cat)=???

    }

    classCatsController(implicitinjector:DomainInjector=newDomainInjector)extendsController{

    importinjector._

    defmomi(catName:String)=Action{

    catRepository.get(catName)foreachdoSomething

    valservice=newCatMomictureServiceservice.momi(catName)

    Ok("")}

    Cache

    Cache(htmljson)

    63GANMA!Cache

  • MapGoogleGuavaCacheBuilderExpireMap

    GANMA!

    64GANMA!Cache

    https://github.com/google/guava

  • PlayFramework

    2

    PlayScala+DDD

    4

    projectRootapplicationdomaininfrastructure

    PlayControllerViewJSONRESTAPIUIPlayViewAndroidiOSWebUI

    PlayPlay

    65PlayFramework

  • build.sbt

    /****/lazyvalroot=(projectinfile(".")).aggregate(application,domain,infrastructure)

    /*****Play*/lazyvalapplication=Project(id="application",base=file("application")).dependsOn(domain,infrastructure).enablePlugins(

    PlayScala)

    /******/lazyvaldomain=Project(id="domain",base=file("domain")).dependsOn(infrastructure).settings(

    scalaSourceinCompile:=baseDirectory.value/"src"/"main"/"scala",scalaSourceinTest:=baseDirectory.value/"src"/"test"/"scala")

    /****/lazyvalinfrastructure=Project(

    66PlayFramework

  • id="infrastructure",base=file("infrastructure")).settings(

    scalaSourceinCompile:=baseDirectory.value/"src"/"main"/"scala",scalaSourceinTest:=baseDirectory.value/"src"/"test"/"scala")

    projectRootapplicationapp/conf/public/target/domainsrc/target/infrastructuresrc/target/project/target/activatorbuild.sbt

    $./activator"projectapplication"run#./activator"application/run"

    $./activator[projectRoot]projectapplication[application]run

    1. High-levelmodulesshouldnotdependonlow-levelmodules.Bothshoulddependonabstractions.()

    2. Abstractionsshouldnotdependondetails.Detailsshoulddependonabstractions.()

    DIP(DependencyInversionPrinciple)

    67PlayFramework

  • DIPDDD

    Play

    ScalaPlayFrameworkDDD

    68PlayFramework

  • (Ports&Adapters)22

    Hexagonalarchitecturehttp://alistair.cockburn.us/Hexagonal+architecture

    1()(4)

    69

    http://alistair.cockburn.us/Hexagonal+architecture

  • 2RESTAPIWEB

    2

    WebServicePlayframework2.4Database

    70

  • SBTSBTrootroot

    build.sbt

    name:="""hexagonal_sample"""

    lazyvalcommonSettings=Seq(scalaVersion:="2.11.7")

    lazyvalroot=(projectinfile("root")).enablePlugins(PlayScala).dependsOn(portWebService,portDatabase).settings(commonSettings).settings(

    routesGenerator:=InjectedRoutesGenerator)

    lazyvalportWebService=Project(id="port-webservice",base=file("port/webservice")).dependsOn(application).settings(commonSettings)

    .settings(/*Port*/)

    lazyvalportDatabase=Project(id="port-database",base=file("port/database")).dependsOn(application).settings(commonSettings)

    .settings(/*Port*/)

    lazyvalapplication=Project(id="application",base=file("application")).enablePlugins(PlayScala).settings(commonSettings)

    .settings(/*Application*/)

    71

  • packagesample.application.user

    caseclassUserId(value:Long)

    classUser(valid:UserId,valname:String,valpassword:String)

    traitUserRepository{

    defresolveBy(id:UserId):Option[User]

    defstore(user:User):Unit}

    classUserRegistrationService(userRepository:UserRepository){

    defregister(name:String,password:String):User={valuser=createNewUser(name,password)userRepository.store(user)user}

    privatedefcreateNewUser(name:String,password:String):User={//???}}

    72

  • Database

    packagesample.port.database.rdbmsadapter.user

    importsample.application.user.{UserRepository,User,UserId}

    classUserRepositoryOnRDBMSextendsUserRepository{

    overridedefresolveBy(id:UserId):Option[User]={//DB???}

    overridedefstore(user:User):Unit={//DB???}}

    WebService

    RESTAPI

    packagesample.port.webservice.restadapter.user

    importcom.google.inject.Injectimportplay.api.data.Formsimportplay.api.data.Formimportplay.api.libs.json._importplay.api.mvc._importsample.application.user._

    caseclassUserDTO(id:Long,name:String)

    objectUserDTO{

    deffromModel(user:User):UserDTO=UserDTO(user.id.value,user.name)

    implicitvaljsonWrites=Json.writes[UserDTO]}

    caseclassUserCreateForm(name:String,password:String)

    objectUserCreateForm{

    73

  • valform:Form[UserCreateForm]=Form(Forms.mapping("name"->Forms.nonEmptyText,"password"->Forms.nonEmptyText)(apply)(unapply))}

    classUserController@Inject()(userRepository:UserRepository,userRegistrationService:UserRegistrationService)extendsController{

    defget(id:Long)=Action{valuser:Option[User]=userRepository.resolveBy(UserId(id))user.fold[Result](NotFound){u=>

    Ok(Json.toJson(UserDTO.fromModel(u)))}}

    defcreate=Action{implicitrequest=>UserCreateForm.form.bindFromRequest().fold(errors=>BadRequest,form=>{

    valuser=userRegistrationService.register(form.name,form.password)Ok(Json.toJson(UserDTO.fromModel(user)))})}}

    74

  • Root

    packagesample

    importcom.google.inject.AbstractModuleimportsample.application.user.{UserRegistrationService,UserRepository}importsample.port.database.rdbmsadapter.user.UserRepositoryOnRDBMS

    classSampleInjectorextendsAbstractModule{

    lazyvaluserRepository:UserRepository=newUserRepositoryOnRDBMS()

    lazyvaluserRegistrationService:UserRegistrationService=newUserRegistrationService(userRepository)

    overridedefconfigure():Unit={bind(classOf[UserRepository]).toInstance(userRepository)

    bind(classOf[UserRegistrationService]).toInstance(userRegistrationService)}}

    application.conf

    play.modules.enabled+="sample.SampleInjector"

    routes

    GET/users/:[email protected](id:Long)POST/users@sample.port.webservice.restadapter.user.UserController.create

    RESTAPIWEB2

    75

  • 76

  • AkkaSchedulerInstagramTumblrAkkaSchedulerInstagramTumblr

    Akka

    Akka

    AkkaScalaJavaScalaTypesafeAkkaActor

    akka-quartz-schedulercronplayframeworkTumblrInsta

    1. build.sbt

    libraryDependencies+="com.enragedginger"%%"akka-quartz-scheduler"%"1.4.0-akka-2.3.x"

    2. application.conf

    akka{quartz{

    defaultTimezone="Asia/Tokyo"schedules{

    AutomationBlog{description="3"expression="000-23/3?**"}}}}

    31cronhttp://quartz-scheduler.org/api/2.1.7/org/quartz/CronExpression.html

    77AkkaSchedulerInstagramTumblr

    http://quartz-scheduler.org/api/2.1.7/org/quartz/CronExpression.html

  • 3. ActorInstagramTumblr

    classGetInstagramImageCreateTumblrextendsActor{

    defreceive={casemsg:String=>valinstaImgUrl=GetHashTagImagevaltumblrResponce=CreateBlog(instaImgUrl)}}

    wGetHashTagImageInstagramCreateBlog(instaImgUrl)URLTumblr

    4. Global

    objectGlobalextendsGlobalSettings{

    valsystem=ActorSystem("SampleSystem")valactor=system.actorOf(Props(classOf[GetInstagramImageCreateTumblr]))

    overridedefonStart(app:Application)={QuartzSchedulerExtension(system).schedule("AutomationBlog",actor,"")}

    overridedefonStop(app:Application)={system.shutdown()}}

    GlobalonStartAutomationBlogapplication.conf

    activatorrunGlobalonStart

    InstagramAPI1.

    InstagramAPIhttps://syncer.jp/instagram-api-matome#sec-1https://syncer.jp/instagram-api-matome#sec-2

    78AkkaSchedulerInstagramTumblr

    https://syncer.jp/instagram-api-matome#sec-1https://syncer.jp/instagram-api-matome#sec-2

  • 2. Instagram1APIhttps://www.instagram.com/developer/endpoints/tags/#get_tags_media_recent

    privatedefGetHashTagImage={//valhashTag="cat"

    //API(count)valapiUrl="https://api.instagram.com/v1/"+"tags/"+hashTag+"/media/recent?count=1&access_token="+"**************************************************"

    //WSjsonvalapiResult=WS.url(apiUrl).get().map{response=>response.json}

    valjson=Await.result(apiResult,Duration.Inf)

    //jsonURLvalimageUrl=json\"data"\\"low_resolution"map(_\"url")valimageUrlStr=imageUrl.map{a=>a.get.toString()}

    //imageUrlStr.head.replace("\"","")}

    InstajsonURLimageslow_resolutionurl

    79AkkaSchedulerInstagramTumblr

    https://www.instagram.com/developer/endpoints/tags/#get_tags_media_recent

  • "images":{"low_resolution":{"height":320,"url":"https://scontent.cdninstagram.com/***.jpg","width":320},

    "standard_resolution":{"height":640,"url":"https://scontent.cdninstagram.com/***.jpg","width":640},

    "thumbnail":{"height":150,"url":"https://scontent.cdninstagram.com/***.jpg","width":150}},

    80AkkaSchedulerInstagramTumblr

  • TumblrAPIInstagramTumblr

    1. Instagramhttps://syncer.jp/tumblr-api-matome#sec-1

    2. OAuthTumblrAPIURLPhotoPostsGETPOSTOAuthhttps://www.tumblr.com/docs/en/api/v2#posting

    3. OAuthtokenTumblrInstagramTwittertokentokenAPITumblrtoken

    4. JumblrApiconsoleJavaJavaTumblrOAuthJumblrscalaJumblr

    5. build.sbtJumblr

    libraryDependencies+="com.tumblr"%"jumblr"%"0.0.11"

    81AkkaSchedulerInstagramTumblr

    https://syncer.jp/tumblr-api-matome#sec-1https://www.tumblr.com/docs/en/api/v2#posting

  • 1.

    privatedefCreateBlog(imageUrl:String)={valconsumerKey="**************************************************"valconsumerSecret="**************************************************"

    valclient:JumblrClient=newJumblrClient(consumerKey,consumerSecret)client.setToken(

    //oauthToken"**************************************************",//oauthTokenSecret"**************************************************")

    //postCreateparamjava.util.MapscalaMapvalparams:java.util.Map[String,String]=newHashMap[String,String]()params.put("type","photo")params.put("source",imageUrl)client.postCreate("instagramtagimage.tumblr.com",params)}

    JumblrimageUrlInstaURLconsumerKeyconsumerSecretoauthTokenoauthTokenSecret4TumblrApiconsole

    82AkkaSchedulerInstagramTumblr

  • Tumblr

    AkkaschedulercronPlay

    TwitterPinterestSNSw

    83AkkaSchedulerInstagramTumblr

  • scalaDDD

    GANMA!onDDD

    DIP:theDependencyInversionPrinciple

    5

    DDD

    84scalaDDD

  • GANMA!Android

    DIP

    85scalaDDD

  • packagecom.sample.domain.magazin.trait

    traitMagazineIdSource{valrawId:String}

    traitMagazineSource{valid:MagazineIdSourcevaltitle:String}

    packagecom.sample.domain.magazine

    importcom.COMICSMART.GANMA.domain.magazine.traits.{MagazineIdSource,MagazineSource}importcom.sample.infra.magazine.MagazineAPIimportscala.concurrent.ExecutionContext.Implicits.globalimportscala.concurrent.Future

    caseclassMagazineId(rawid:String)extendsMagazineIdSource

    //caseclassMagazine(id:MagazineId,title:String)extendsMagazineSource

    //MagazineobjectMagazine{defapply(src:MagazineSource):Magazine={Magazine(MagazineId(src.id.rawId),src.title)}}

    classMagazineRepository(api:MagazineApi=MagazineAPI()){

    defget(magazineId:MagazineId):Future[Magazine]=api.get(magazineId).map(Magazine(_))

    }

    86scalaDDD

  • packagecom.sample.infra.magazine

    importcom.COMICSMART.GANMA.domain.magazine.traits.{MagazineIdSource,MagazineSource}importscala.concurrent.ExecutionContext.Implicits.globalimportscala.concurrent.Future

    classMagazineAPI(){

    defget(magazineId:MagazineIdSource):Future[MagazineSource]={//httpjson}}

    GANMA!AndroidView

    packagecom.sample.application.magazine

    importandroid.app.Activityimportandroid.os.Bundleimportcom.sample.domain.magazine.{MagazineId,Magazine}importcom.sample.domain.magazine.MagazineRepositoryimportscala.concurrent.ExecutionContext.Implicits.global

    classMagazineActivityextendsActivity{

    valmagazineRepository=MagazineRepository()

    overridedefonCreate(savedInstanceState:Bundle):Unit={super.onCreate(savedInstanceState)

    valmagazineId=MagazineId(getIntent.getStringExtra("magazineId"))magazineRepository.get(magazineId).onSuccess{casem:Magazine=>//viewcase_=>}

    }

    ------

    }

    87scalaDDD

  • DDD

    VaughnVernon(2015)()

    http://d.hatena.ne.jp/asakichy/20090128/1233144989

    http://www.atmarkit.co.jp/fdotnet/designptn/designptn07/designptn07_01.html

    88scalaDDD

    http://d.hatena.ne.jp/asakichy/20090128/1233144989http://www.atmarkit.co.jp/fdotnet/designptn/designptn07/designptn07_01.html

  • AndroidKotlin1010MANTAndroid

    2015AndroidKotlinMANT

    KotlinAndroidAndroid10

    1.setOnClickListener

    inlinefunT.onClick(crossinlinef:(T)->Unit)=setOnClickListener{f(this)}

    //button.onClick{/*dosomething*/}

    2.

    funContext.loadColor(@ColorResresId:Int)=ContextCompat.getColor(this,resId)

    //view.setBackgroundColor(loadColor(R.color.orange))

    3.

    funActivity.hideKeyoard()=(getSystemService(Context.INPUT_METHOD_SERVICE)asInputMethodManager).hideSoftInputFromWindow(currentFocus.windowToken,0)

    //hideKeyboard()

    89AndroidKotlin10

  • 4.Fragmentreplace

    funFragmentManager.replace(resId:Int=R.id.default_content,fragment:Fragment,tag:String?=null,stack:String?=null){begiTransaction().replace(resId,fragment,tag).addToBackStack(stack).commit()}

    //supportFragmentManager.replace(fragment=MyFragment())

    5.JSONObject

    funJSONObject.pick(key:String):T?=try{get(key)as?T}catch(e:JSONException){null}

    //valfirstName=json.pick("firstName")

    6.LoaderCallbackinitrestart

    funLoaderManager.init(id:Int=0,args:Bundle?=null,onCreate:(Int,Bundle?)->Loader,onLoadFinish:(Loader?,T)->Unit,onLoadReset:(Loader?)->Unit={})=initLoader(id,args,object:LoaderManager.LoaderCallbacks{overridefunonCreateLoader(id:Int,args:Bundle?):Loader?=onCreate(id,args)overridefunonLoadFinished(loader:Loader?,data:T)=onLoadFinish(loader,data)overridefunonLoadReset(loader:Loader?)=onLoadReset(loader)})

    //supportLoaderManager.init(onCreate={id,args->createNewLoader()}onLoadFinish={loader,c->bindData(c)})

    90AndroidKotlin10

  • 7.ScrollListener

    funT.onScroll(onStateChange:(T?,Int)->Unit={view,scrollState->}onScroll:(T?,Int,Int,Int)->Unit={view,f,v,t->}){setOnScrollListener(object:AbsListView.OnScrollListener{overridefunonScrollStateChanged(view:AbsListView?,scrollState:Int){onStateChange(viewasT,scrollState)}

    overridefunonScroll(view:AbsListView?,firstVisibleItem:Int,visibleItemCount:Int,totalItemCount:Int){onScroll(viewasT,firstVisibleItem,visibleItemCount,totalItemCount)}})}

    //listView.onScroll(onStateChange={view,scrollState->/*dosomething*/})

    8.ImageView

    funImageView.setImage(source:Any?){source?.javaClass?.name

    when(source){null->setImageBitmap(null)isInt->setImageResource(source)isBitmap->setImageBitmap(source)isDrawable->setImageDrawable(source)isUri->setImageURI(source)else->throwIllegalArgumentException("Thissourcetypeof${source.javaClass.name}isnotsupported")}}

    //imageView1.setImage(bitmap)imageView2.setImage(R.drawable.icon)

    91AndroidKotlin10

  • 9.Animator.AnimationListener

    funViewPropertyAnimator.listener(onStart:(Animator?)->Unit={},onCancel:(Animator?)->Unit={},onRepeat:(Animator?)->Unit={},onEnd:(Animator?)->Unit={}){setListener(object:Animator.AnimatorListener{overridefunonAnimationStart(a:Animator?)=onStart(a)overridefunonAnimationCancel(a:Animator?)=onCancel(a)overridefunonAnimationRepeat(a:Animator?)=onRepeat(a)overridefunonAnimationEnd(a:Animator?)=onEnd(a)})}

    //view.animate().x(80f).listener(onStart={view.alpha=0.5f},onEnd={view.alpha=1.0f})

    10.ViewTreeObserber.OnGlobalLayoutListener

    inlinefunView.newGlobalLayoutListener(crossinlinef:(ViewTreeObserver.OnGlobalLayoutListener)->Unit):ViewTreeObserver.OnGlobalLayoutListener{

    returnobject:ViewTreeObserver.OnGlobalLayoutListener{overridefunonGlobalLayout()=f(this)}.apply{viewTreeObserver.addOnGlobalLayoutListener(this)}}

    funView.removeGlobalLayoutListener(listener:ViewTreeObserver.OnGlobalLayoutListener)=viewTreeObserver.addOnGlobalLayoutListener(listener)

    //view.newGlobalLayoutListener{textView.visibility=View.VISIBLEview.removeGlobalLayoutListener(it)}

    92AndroidKotlin10

  • Scala

    http://septeni-original.co.jp

    http://tour.septeni.net/

    SeptenixScalaconnpassScala

    http://septeni-scala.connpass.com/

    Scala-Scala/-ScalaDDD-GANMA!DDD1-DDD-

    URLconnpass

    93

    http://septeni-original.co.jphttp://tour.septeni.net/http://septeni-scala.connpass.com/

    PYXISIntelliJIncoming WebHooksScalaSlackBeanstalk for DockerPlay FrameworkPythonScalaGANMA!CachePlay FrameworkAkka SchedulerInstagramTumblrscala DDD AndroidKotlin10