メインで利用しているクラウドを AWS から Salesforce に切り替えた話

どうも、三日坊主をやめた Xu です。
なぜ三日坊主をやめたかというと、実は最近は個人用 Salesforce 組織で個人開発のタスクや Qiita 記事の管理をしているんです！

## Salesforce で個人開発⁉
Salesforce は世間でも広く知られている CRM サービスです。

なかなかこれを個人開発で使おうと思う人は少ないかもですが、基本機能では例えば、ToDo の Kanban ビューで Bot 開発から DevOps プロジェクトまで自己管理が手軽にできてしまう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2466211/4a011692-0e29-4dba-b1fe-6fb14308d221.png)
※個人で使う分にはいいと思いますが、比較的機能が単一なのでチーム開発ではお勧めしません。

そのほかにもクラウドとして利用できる利点が多く、実は最近色々と Salesforce に移行したりしてて、AWS の費用がめちゃんこ抑えられました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2466211/11eb58f2-0d58-4cd2-8fc9-c1eb245b97f2.png)
月 4000 円近くの出費をほぼ 0 にできたので家内に褒められてもいいのでは？

個人で Salesforce を使うメリットとしては：
- アドオンや企業利用でなければ基本機能は完全無料
- DB を自前で用意する必要がない
- スケジューラーが提供されている
- 高性能なレポート・ダッシュボード機能で統計データなどわかりやすく管理・プレゼン・共有することができる
- 提供されている API が豊富であるため、インテグレーションがしやすい
- （個人で開発に使ってる狂人が少ないから記事のネタになる）

デメリットとしては：
- Apex 書くのがめんどくさい
- Apex のデバッグがめんどくさい
- データ処理がもっさりしている
- 無料のデベロッパー組織だと利用できない機能がある
- アプリケーションをホストして公開することはできない

## 参考記事を探す
さーて、Salesforce で開発をするぞと決めたならまずはみんなどうやっているのか知りたくなりますよね。
Salesforce を使った個人開発事例は実はあまりなく（記事としてあんまり出回ってはない気がする）

例えばこちらの記事では Salesforce を使って日常的に使える家計簿アプリを作ろうとしていますが、続きがないのでどうなったとやら。。。
[Salesforceを使って家計簿をつける](https://zenn.dev/fukako/articles/b68c5d4b73905c)

## とりあえずやってみる
AWS Lambda で走ってた毎朝天気を通知してくれる Bot をまず移行しようと思いました。

構成としてはもともと AWS -> LINE Developer platform だったものを Salesforce Apex Callout -> LINE Developer Platform にするだけです。

とりあえず天気情報を持ってきて LINE に送信する機能を実行する：
```Apex Class HttpCalloutDailyWeather.cls
public class HttpCalloutDailyWeather {
    public class Min {
        public String celsius;
    }
    
    public class Max {
        public String celsius;
    }
    
    public class Temperature {
        public Min min;
        public Max max;
    }
    
    public class ChanceOfRain {
        public String T00_06;
        public String T06_12;
        public String T12_18;
        public String T18_24;
    }
    
    public class Forecast {
        public String telop;
        public Temperature temperature;
        public ChanceOfRain chanceOfRain;
    }
    
    public class WeatherResponse {
        public List<Forecast> forecasts;
    }
    
    public static void notifyBot(String weatherres) {
        Http h = new Http();
        String url = 'https://api.line.me/v2/bot/message/broadcast';
        HttpRequest req = new HttpRequest();
        req.setEndpoint(url);
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/json');
        req.setHeader('Authorization', 'Bearer LINE_BOT_TOKEN');
        String data = '{"messages": [{"type": "text","text": "' + weatherres +'"}]}';
        req.setBody(data);
        HttpResponse res = h.send(req);
    }

    @future(callout=true)
    public static void getWeatherForecast() {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        String url = 'https://weather.tsukumijima.net/api/forecast/city/130010';
        request.setEndpoint(url);
        request.setMethod('GET');
        HttpResponse response = http.send(request);
        if (response.getStatusCode() == 200) {
            WeatherResponse weatherData = (WeatherResponse) JSON.deserialize(response.getBody(), WeatherResponse.class);
            Forecast todayForecast = weatherData.forecasts[0];
            String tempMin = todayForecast.temperature.min.celsius;
            String tempMax = todayForecast.temperature.max.celsius;
            String cor_06 = todayForecast.chanceOfRain != null && todayForecast.chanceOfRain.T00_06 != null ? todayForecast.chanceOfRain.T00_06 : '--';
            String cor_612 = todayForecast.chanceOfRain != null && todayForecast.chanceOfRain.T06_12 != null ? todayForecast.chanceOfRain.T06_12 : '--';
            String cor_1218 = todayForecast.chanceOfRain != null && todayForecast.chanceOfRain.T12_18 != null ? todayForecast.chanceOfRain.T12_18 : '--';
            String cor_1824 = todayForecast.chanceOfRain != null && todayForecast.chanceOfRain.T18_24 != null ? todayForecast.chanceOfRain.T18_24 : '--';
            
            String weatherres = todayForecast.telop + '\\n気温: ' + tempMin + '-' + tempMax + '\\n0-6: ' + cor_06 + '\\n6-12: ' + cor_612 + '\\n12-18: ' + cor_1218 + '\\n18-24: ' + cor_1824;
            notifyBot(weatherres);
        }
    }
}
```

見ての通り、都度構造を作ってあげないといけないのはかなりめんどくさいですよね。

しかもこのままでは Apex をスケジュールすることはできません。
スケジューラークラス内では非同期処理を実装できないため、別でスケジュールできてこのクラスを呼び出すクラスを作らないといけない。
```Apex Class　DailyBotScheduler.cls
global class DailyBotScheduler implements Schedulable {
    global void execute(SchedulableContext sc) {
        HttpCalloutDailyWeather.getWeatherForecast();
    }
}
```

Apex スケジュールを組むことで毎朝天気予報が無料で届くようになりました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2466211/e543d749-28c2-4919-8c57-ae4f797c53bf.png)
AWS Lambda だったら構築一瞬で終わってたけどね💦

## あとがき
めんどくさいの一言ですが、まあ世間でいうノーコード・ローコードって結局コード書ける人が裏で頑張ってるんだなと思い知らされました。

この調子でどんどん開発を進んでいこうかなと思います。
どうか応援をよろしくお願いいたします。